if (FALSE) {
  installPackagesNeeded()
}

Processo

Um paciente que está em lista de espera para ser operado:

  1. Entra em contacto com o serviço de cirurgia, vários dias antes da sua operação,
  1. Os serviços administrativos atendem as chamadas ou lêem a mensagem via app e, mediante

Contudo, este procedimento necessita de melhorias. O objectivo deste estudo é optimizar a gestão dos contactos (tempo total do processo e total de chamadas perdidas)

Situação atual

  1. Apenas 2 administrativos no serviço, ambos começam às 8h e acabam às 18h (10h de trabalho) de cada dia útil.

  2. O tempo entre chegadas tem exp(1/40) (pq media de 40/h)

  3. Tipo de contacto:

  1. Tipo de consulta:
  1. Tempo de
  1. 5o telefone desliga apos 5 minutos de espera (e fica chamada perdida)
library(simmer)
library(simmer.bricks)
library(simmer.plot)
conflicts_prefer(simmer::rollback)
[conflicted] Removing existing preference.[conflicted] Will prefer simmer::rollback over any other package.
conflicts_prefer(simmer::now)
[conflicted] Removing existing preference.[conflicted] Will prefer simmer::now over any other package.
conflicts_prefer(dplyr::select)
[conflicted] Removing existing preference.[conflicted] Will prefer dplyr::select over any other package.
# this will be substituted below and literally just is here because simmer doesn't know how to implement pipes and I hate it here
env <- simmer("hospital") 

Resources: Server: O hospital com 2 administradores Queue: Fila de espera com prioridade de clients em chamada Não há vantgem em escolher a app quando há chamada de espera Manager: vai atribuir o tipo de consulta (e Vai alterar o tipo de cliente se ele for rejeitado na chamada?) Gerador: vai gerir novos arrivals (exp(1/40)) arrival: cada paciente (que leva uma trajectory) trajectory: a receita de cada arrival

# atributes need to be numeric
tipo_de_consulta.PRESENCIAL <- 1
tipo_de_consulta.TELEFONICA <- 2
tipo_de_consulta.NAO_AGENDADA <- 3
tipos_de_consulta <- c(tipo_de_consulta.PRESENCIAL, tipo_de_consulta.TELEFONICA, tipo_de_consulta.NAO_AGENDADA)
tipos_de_consulta.probs <- c(0.1, 0.6, 0.3)

tipo_de_contacto.TELEFONE <- 1
tipo_de_contacto.APP <- 2
tipos_de_contacto <- c(tipo_de_contacto.TELEFONE, tipo_de_contacto.APP)
tipos_de_contacto.probs <- c(0.85, 0.15)

tempo_atendido <- function() {
  mean_time_triagem <- rnorm(1, mean = 4, sd = 1)
  mean_time_decision <- rnorm(1, mean = 1, sd = 0.25)
  time_to_timeout <- mean_time_triagem + mean_time_decision
  
  if (get_attribute(env, "tipo_de_consulta") != tipo_de_consulta.NAO_AGENDADA) {
    time_to_timeout <- time_to_timeout + rnorm(1, mean = 1, sd = 0.25)
  }
  
  return(time_to_timeout)
}

intime <- function() {
  now(env)%% 24*60 -> n
  return(8*60 < n & n < 18*60)
}
pessoa <- trajectory("pessoa") %>%
  # set_attribute("times_phone_rejected", 0) %>% 
  # set_attribute("phone_rejected", 0) %>%
  set_attribute("tipo_de_consulta", \() sample(tipos_de_consulta, 1, prob = tipos_de_consulta.probs)) %>%
  set_attribute("tipo_de_contacto", \() sample(tipos_de_contacto, 1, prob = tipos_de_contacto.probs), tag = "contacto") %>%
  set_prioritization(\() if (get_attribute(env, "tipo_de_contacto") == tipo_de_contacto.TELEFONE) c(1, -1, FALSE) else c(0 , -1, FALSE)) %>% # PRIORITY, DROP PRIORITY, RESTART
  # log_(\() paste("Vou pra fila agora, estou a usar", ifelse(get_attribute(env, "tipo_de_contacto") == tipo_de_contacto.TELEFONE, "o telefone", "a app"))) %>%
  renege_in(\() ifelse(get_attribute(env, "tipo_de_contacto") == tipo_de_contacto.TELEFONE, 5, Inf), out = 
              trajectory() %>% set_attribute("phone_rejected", 1)
              # O botas não gostou
              # trajectory("pessoa_try_again") %>% 
              # set_attribute("times_phone_rejected", \() get_attribute(env, "times_phone_rejected") + 1) %>% 
              # # leave if out of schedule
              # leave(\() {ifelse(intime(), 0, 1)}) %>% 
              # # log_(\() "Nao fui atendido, vou voltar pra fila") %>% 
              # rollback(target="contacto", times = 1) # não funciona, mudar pra numero?
            ) %>%
  seize("administrador", 1) %>%
  renege_abort() %>%
  # log_(\() paste("Before timeout")) %>% 
  set_attribute("atendido", 1) %>% 
  timeout(tempo_atendido) %>% 
  # log_(\() "After timeout") %>% 
  release("administrador", 1)
  # log_(\() "Leaving")

pessoa
trajectory: pessoa, 10 activities
{ Activity: SetAttribute | keys: [tipo_de_consulta], values: function(), global: 0, mod: N, init: 0 }
{ Activity: SetAttribute | [contacto] keys: [tipo_de_contacto], values: function(), global: 0, mod: N, init: 0 }
{ Activity: SetPrior     | values: function(), mod: N }
{ Activity: RenegeIn     | t: function(), keep_seized: 0 }
  Fork 1, stop,  trajectory: anonymous, 1 activities
  { Activity: SetAttribute | keys: [phone_rejected], values: [1], global: 0, mod: N, init: 0 }
{ Activity: Seize        | resource: administrador, amount: 1 }
{ Activity: RenegeAbort  |  }
{ Activity: SetAttribute | keys: [atendido], values: [1], global: 0, mod: N, init: 0 }
{ Activity: Timeout      | delay: function() }
{ Activity: Release      | resource: administrador, amount: 1 }
set.seed(1)
# 50 replicas
envs <- lapply(1:50, function(i) {
  env <<- simmer("hospital") 
  env %>% 
    add_generator("pessoa", pessoa, from_to(8*60, 18*60, \() rexp(1, 40/60), every = 24*60, arrive = F), mon = 2) %>% 
    add_resource("administrador", schedule(c(8*60, 18*60), c(2, 0), period = 24*60)) %>%
    run(24*60*5)
})

envs %>% simmer.plot::get_mon_arrivals(ongoing = T) -> arrivals 
envs %>% simmer.plot::get_mon_attributes() -> attributes
envs %>% simmer.plot::get_mon_resources() -> resources

arrivals %>%
  filter(start_time != -1) %>% # n sei pq e q isto acontece
  arrange(start_time) %>% 
  select(-activity_time) %>% 
  mutate(
    day = ceiling(start_time/(24*60)),
    start_time_day = start_time %% (24*60) %>% seconds_to_period(),
    end_time_day = end_time %% (24*60) %>% seconds_to_period(),
    # replication = replication %>% as_factor(),
  ) -> arrivals.df1
arrivals.df1 %>% filter(replication == 1) %>% mutate(start_time_day = start_time_day %>% round(3), end_time_day = end_time_day %>% round(3))



# turn what's above into a function so it can be done for 2a and 2b
# get_arrivals <- function(mon_attributes) {
#   mon_attributes %>%
#     filter(start_time != -1) %>% # n sei pq e q isto acontece
#     arrange(start_time) %>% 
#     select(-activity_time) %>% 
#     mutate(
#       day = ceiling(start_time/(24*60)),
#       start_time_day = start_time %% (24*60) %>% seconds_to_period(),
#       end_time_day = end_time %% (24*60) %>% seconds_to_period(),
#       # replication = replication %>% as_factor(),
#     )
# }
# 
# pprint_arrivals <- function (arrivals.df1) {
#   arrivals.df1 %>% filter(replication == 1) %>% mutate(start_time_day = start_time_day %>% round(3), end_time_day = # end_time_day %>% round(3))
# }
# analise de eficiencia
# numero de chamadas perdidas
# add day to atributes from arrivals.df1
attributes %>% 
  left_join(arrivals.df1 %>% select(name, replication, day, start_time, end_time, end_time_day), by = c("name", "replication")) %>% 
  rename(atribute_time = time, pessoa_start_time = start_time, pessoa_end_time = end_time) %>% 
  select(atribute_time, name, key, value, replication, day, pessoa_start_time, pessoa_end_time) -> attributes.df1
attributes.df1 
# pessoas e os seus tipos de chamada
attributes.df1 %>% 
  filter(key == "tipo_de_contacto") %>% 
  select(name, replication, value) %>% 
  mutate(value = ifelse(value == tipo_de_contacto.TELEFONE, "telefone", "app")) %>%
  rename(contacto = value) -> contactos.df1
# percentagem de chamadas perdidas
attributes.df1 %>% 
  filter(key == "tipo_de_contacto", value == tipo_de_contacto.TELEFONE) %>% 
  left_join(attributes.df1 %>% filter(key == "phone_rejected") %>% mutate(gave_up = key) %>% select(name, replication, gave_up), join_by("name", "replication")) %>% 
  mutate(gave_up = if_else(gave_up %>% is.na, T, F), replication = replication %>% as_factor) %>% 
  select(name, replication, pessoa_end_time, gave_up) -> phone_calls
phone_calls
# phone_calls rate per replication
phone_calls %>% 
  group_by(replication) %>% 
  summarise(phone_calls = n(), gave_up = sum(gave_up)) %>% 
  mutate(phone_calls_per_hour = phone_calls/10, gave_up_rate = gave_up/phone_calls) %>% 
  arrange(desc(gave_up_rate)) -> phone_calls_rate
phone_calls_rate

phone_calls_rate %>% 
  ggplot(aes(x = reorder(replication, gave_up_rate), y = gave_up_rate)) +
  geom_col() + 
  ylim(0, 1) +
  theme(axis.text.x = element_text(angle = 70, hjust = 1)) +
  geom_hline(aes(yintercept = min(gave_up_rate)), linetype = "dashed", color = "red") +
  geom_hline(aes(yintercept = max(gave_up_rate)), linetype = "dashed", color = "blue") +
  xlab("Replication") + 
  ylab("Gave up rate")

ggsave("gave_up_rate.svg")
Saving 7.29 x 4.5 in image

phone_calls_rate %>% 
  summarise(
    min = min(phone_calls_per_hour), 
    mean = mean(phone_calls_per_hour), 
    max = max(phone_calls_per_hour)
    )
# evolucao ao longo do dia DO QUE?
attributes.df1 %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none")

# get queue size histogram
resources %>% 
  mutate(day = ceiling(time/(24*60))) %>% 
  group_by(replication) %>%
  reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
  mutate(replication = replication %>% as_factor()) %>% 
  ggplot(aes(x = time, y = queue_size)) +
  geom_step(aes(color = replication), alpha = 0.3) + 
  geom_smooth(method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none")


resources %>% 
  mutate(day = ceiling(time/(24*60))) %>% 
  group_by(replication) %>%
  reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
  mutate(replication = replication %>% as_factor()) %>% 
  ggplot(aes(x = time, y = queue_size)) +
  geom_step(aes(color = replication), alpha = 0.3) + 
  geom_smooth(method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  ylim(0,250)

NA
NA
# join these 2 plots
attributes.df1 %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  geom_step(data = resources %>% 
              mutate(day = ceiling(time/(24*60))) %>% 
              group_by(replication) %>%
              reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
              mutate(replication = replication %>% as_factor()), 
            aes(x = time, y = queue_size), alpha = 0.3) + 
  geom_smooth(data = resources %>% 
                mutate(day = ceiling(time/(24*60))) %>% 
                group_by(replication) %>%
                reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
                mutate(replication = replication %>% as_factor()), 
              aes(x = time, y = queue_size), method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  ylim(0,875)

  
attributes.df1 %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  geom_step(data = resources %>% 
              mutate(day = ceiling(time/(24*60))) %>% 
              group_by(replication) %>%
              reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
              mutate(replication = replication %>% as_factor()), 
            aes(x = time, y = queue_size), alpha = 0.3) + 
  geom_smooth(data = resources %>% 
                mutate(day = ceiling(time/(24*60))) %>% 
                group_by(replication) %>%
                reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
                mutate(replication = replication %>% as_factor()), 
              aes(x = time, y = queue_size), method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none")



attributes.df1 %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +

  theme(legend.position = "none") +
  geom_step(data = resources %>% 
              mutate(day = ceiling(time/(24*60))) %>% 
              group_by(replication) %>%
              reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
              mutate(replication = replication %>% as_factor()), 
            aes(x = time, y = queue_size), alpha = 0.3) + 
  geom_smooth(data = resources %>% 
                mutate(day = ceiling(time/(24*60))) %>% 
                group_by(replication) %>%
                reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
                mutate(replication = replication %>% as_factor()), 
              aes(x = time, y = queue_size), method="lm") +

  theme(legend.position = "none")

# tempo de espera daqueles que concluiram, speardao por chamada e mensagem
attributes %>% 
  filter(key == "atendido") %>% 
  left_join(arrivals.df1 %>% select(start_time, name, replication), join_by("name", "replication")) %>% 
  left_join(contactos.df1, join_by("name", "replication")) %>% 
  mutate(time_waiting = time - start_time) %>% 
  select(name, value, replication, time_waiting, contacto) -> waiting_times
waiting_times
# histograma (n tou a perceber pq é q há waiting times maiores que 5)
waiting_times %>% 
  filter(contacto == "telefone") %>%
  ggplot(aes(x = time_waiting)) +
  geom_histogram(bins = 100) +
  theme(legend.position = "none")


waiting_times %>% 
  filter(contacto == "app") %>%
  ggplot(aes(x = time_waiting)) +
  geom_histogram(bins = 100) +
  theme(legend.position = "none")

# regular waiting time stats without graphs
waiting_times %>% 
  group_by(contacto) %>% 
  summarise(mean = mean(time_waiting), sd = sd(time_waiting), median = median(time_waiting)) 
waiting_times %>% 
  group_by(contacto) %>% 
  summarise(quantile_10 = quantile(time_waiting, 0.1), quantile_20 = quantile(time_waiting, 0.2), quantile_30 = quantile(time_waiting, 0.3), quantile_40 = quantile(time_waiting, 0.4), quantile_50 = quantile(time_waiting, 0.5), quantile_60 = quantile(time_waiting, 0.6), quantile_70 = quantile(time_waiting, 0.7), quantile_80 = quantile(time_waiting, 0.8), quantile_90 = quantile(time_waiting, 0.9), quantile_95 = quantile(time_waiting, 0.95), quantile_99 = quantile(time_waiting, 0.99))
resources %>% plot(metric = "utilization")

resources %>% plot(metric = "usage", steps = T)

Pergunta 2

Alíneas com mudanças relativas às interrupções

# mudanças
envs <- lapply(1:50, function(i) {
  env <<- simmer("hospital") 
  env %>% 
    add_generator("pessoa_de_manha", pessoa, from_to(8*60, 10*60, \() rexp(1, (120/2)/60), every = 24*60, arrive = F), mon = 2) %>% 
    add_generator("pessoa_de_tarde", pessoa, from_to(10*60, 16*60, \() rexp(1, (240/6)/60), every = 24*60, arrive = F), mon = 2) %>% 
    add_generator("pessoa_de_noite", pessoa, from_to(16*60, 18*60, \() rexp(1, (40/2)/60), every = 24*60, arrive = F), mon = 2) %>% 
    add_resource("administrador", schedule(
      c(8 , 9 , 9.5 , 12 , 15.5 ,18)*60, 
      c(  1 , 2,    4,   3,     1,  0), 
      period = 24*60
    )) %>%
    run(24*60*5)
})

envs %>% simmer.plot::get_mon_arrivals(ongoing = T) -> arrivals
envs %>% simmer.plot::get_mon_attributes() -> attributes
envs %>% simmer.plot::get_mon_resources() -> resources

2.b)

# b)
arrivals %>%
  filter(start_time != -1) %>% # n sei pq e q isto acontece
  arrange(start_time) %>% 
  select(-activity_time) %>% 
  mutate(
    day = ceiling(start_time/(24*60)),
    start_time_day = start_time %% (24*60) %>% seconds_to_period(),
    end_time_day = end_time %% (24*60) %>% seconds_to_period(),
    replication = replication %>% as_factor(),
  ) -> arrivals.df2b
arrivals.df2b %>% filter(replication == 1) %>% mutate(start_time_day = start_time_day %>% round(3), end_time_day = end_time_day %>% round(3))
arrivals.df2b$replication <- as.integer(as.character(arrivals.df2b$replication))

# tempo de espera daqueles que concluiram, speardao por chamada e mensagem
arrivals.df2b %>% 
  filter(finished == TRUE) %>% 
  mutate(waiting_time = end_time - start_time) %>% 
  left_join(attributes %>% filter(key == "tipo_de_contacto"), by = c("name", "replication")) %>% 
  mutate(tipo_de_contacto = ifelse(value == tipo_de_contacto.TELEFONE, "telefone", "mensagem") %>% as_factor, replication = replication %>% as_factor) %>%
  select(name, replication, waiting_time, tipo_de_contacto) -> waiting_times.2b
waiting_times.2b
# analise de eficiencia
# numero de chamadas perdidas
# add day to atributes from arrivals.df2b
attributes %>% 
  left_join(arrivals.df2b %>% select(name, replication, day, start_time, end_time, end_time_day), by = c("name", "replication")) %>% 
  rename(atribute_time = time, pessoa_start_time = start_time, pessoa_end_time = end_time) %>% 
  select(atribute_time, name, key, value, replication, day, pessoa_start_time, pessoa_end_time) -> attributes.df2b
attributes.df2b 
# pessoas e os seus tipos de chamada
attributes.df2b %>% 
  filter(key == "tipo_de_contacto") %>% 
  select(name, replication, value) %>% 
  mutate(value = ifelse(value == tipo_de_contacto.TELEFONE, "telefone", "app")) %>%
  rename(contacto = value) -> contactos.df2b
# percentagem de chamadas perdidas
attributes.df2b %>% 
  filter(key == "tipo_de_contacto", value == tipo_de_contacto.TELEFONE) %>% 
  left_join(attributes.df2b %>% filter(key == "phone_rejected") %>% mutate(gave_up = key) %>% select(name, replication, gave_up), join_by("name", "replication")) %>% 
  mutate(gave_up = if_else(gave_up %>% is.na, T, F), replication = replication %>% as_factor) %>% 
  select(name, replication, pessoa_end_time, gave_up) -> phone_calls
phone_calls
# phone_calls rate per replication
phone_calls %>% 
  group_by(replication) %>% 
  summarise(phone_calls = n(), gave_up = sum(gave_up)) %>% 
  mutate(phone_calls_per_hour = phone_calls/10, gave_up_rate = gave_up/phone_calls) %>% 
  arrange(desc(gave_up_rate)) -> phone_calls_rate
phone_calls_rate

phone_calls_rate %>% 
  ggplot(aes(x = reorder(replication, gave_up_rate), y = gave_up_rate)) +
  geom_col() + 
  ylim(0, 1) +
  theme(axis.text.x = element_text(angle = 70, hjust = 1)) +
  geom_hline(aes(yintercept = min(gave_up_rate)), linetype = "dashed", color = "red") +
  geom_hline(aes(yintercept = max(gave_up_rate)), linetype = "dashed", color = "blue") +
  xlab("Replication") + 
  ylab("Gave up rate")

ggsave("gave_up_rate.svg")
Saving 7.29 x 4.5 in image

phone_calls_rate %>% 
  summarise(
    min = min(phone_calls_per_hour), 
    mean = mean(phone_calls_per_hour), 
    max = max(phone_calls_per_hour)
    )
# evolucao ao longo do dia DO QUE?
attributes.df2b %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none")

# get queue size histogram
resources %>% 
  mutate(day = ceiling(time/(24*60))) %>% 
  group_by(replication) %>%
  reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
  mutate(replication = replication %>% as_factor()) %>% 
  ggplot(aes(x = time, y = queue_size)) +
  geom_step(aes(color = replication), alpha = 0.3) + 
  geom_smooth(method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none")


resources %>% 
  mutate(day = ceiling(time/(24*60))) %>% 
  group_by(replication) %>%
  reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
  mutate(replication = replication %>% as_factor()) %>% 
  ggplot(aes(x = time, y = queue_size)) +
  geom_step(aes(color = replication), alpha = 0.3) + 
  geom_smooth(method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  ylim(0,250)

NA
# join these 2 plots
attributes.df2b %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  geom_step(data = resources %>% 
              mutate(day = ceiling(time/(24*60))) %>% 
              group_by(replication) %>%
              reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
              mutate(replication = replication %>% as_factor()), 
            aes(x = time, y = queue_size), alpha = 0.3) + 
  geom_smooth(data = resources %>% 
                mutate(day = ceiling(time/(24*60))) %>% 
                group_by(replication) %>%
                reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
                mutate(replication = replication %>% as_factor()), 
              aes(x = time, y = queue_size), method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  ylim(0,875)

  
attributes.df2b %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  geom_step(data = resources %>% 
              mutate(day = ceiling(time/(24*60))) %>% 
              group_by(replication) %>%
              reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
              mutate(replication = replication %>% as_factor()), 
            aes(x = time, y = queue_size), alpha = 0.3) + 
  geom_smooth(data = resources %>% 
                mutate(day = ceiling(time/(24*60))) %>% 
                group_by(replication) %>%
                reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
                mutate(replication = replication %>% as_factor()), 
              aes(x = time, y = queue_size), method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none")



attributes.df2b %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +

  theme(legend.position = "none") +
  geom_step(data = resources %>% 
              mutate(day = ceiling(time/(24*60))) %>% 
              group_by(replication) %>%
              reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
              mutate(replication = replication %>% as_factor()), 
            aes(x = time, y = queue_size), alpha = 0.3) + 
  geom_smooth(data = resources %>% 
                mutate(day = ceiling(time/(24*60))) %>% 
                group_by(replication) %>%
                reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
                mutate(replication = replication %>% as_factor()), 
              aes(x = time, y = queue_size), method="lm") +

  theme(legend.position = "none")

# tempo de espera daqueles que concluiram, speardao por chamada e mensagem
attributes %>% 
  filter(key == "atendido") %>% 
  left_join(arrivals.df2b %>% select(start_time, name, replication), join_by("name", "replication")) %>% 
  left_join(contactos.df2b, join_by("name", "replication")) %>% 
  mutate(time_waiting = time - start_time) %>% 
  select(name, value, replication, time_waiting, contacto) -> waiting_times
waiting_times
# histograma (n tou a perceber pq é q há waiting times maiores que 5)
waiting_times %>% 
  filter(contacto == "telefone") %>%
  ggplot(aes(x = time_waiting)) +
  geom_histogram(bins = 100) +
  theme(legend.position = "none")+
  ylim(0, 1000)
ggsave("2b_HistTel.svg")
Saving 7.29 x 4.5 in image

waiting_times %>% 
  filter(contacto == "app") %>%
  ggplot(aes(x = time_waiting)) +
  geom_histogram(bins = 100) +
  theme(legend.position = "none")

# regular waiting time stats without graphs
waiting_times %>% 
  group_by(contacto) %>% 
  summarise(mean = mean(time_waiting), sd = sd(time_waiting), median = median(time_waiting)) 
waiting_times %>% 
  group_by(contacto) %>% 
  summarise(quantile_10 = quantile(time_waiting, 0.1), quantile_20 = quantile(time_waiting, 0.2), quantile_30 = quantile(time_waiting, 0.3), quantile_40 = quantile(time_waiting, 0.4), quantile_50 = quantile(time_waiting, 0.5), quantile_60 = quantile(time_waiting, 0.6), quantile_70 = quantile(time_waiting, 0.7), quantile_80 = quantile(time_waiting, 0.8), quantile_90 = quantile(time_waiting, 0.9), quantile_95 = quantile(time_waiting, 0.95), quantile_99 = quantile(time_waiting, 0.99))
resources %>% plot(metric = "utilization")

resources %>% plot(metric = "usage", steps = T)

2.a) - - - - - - - - - - - -

# a) set_prioritization(\() if (get_attribute(env, "tipo_de_consulta") == tipo_de_consulta.TELEFONICA) c(1, -1, FALSE) else c(0 , -1, FALSE))
pessoa__ <- join(
  pessoa[1:2], 
  trajectory("change_prio") %>% 
     set_prioritization(\() if (get_attribute(env, "tipo_de_contacto") == tipo_de_contacto.TELEFONE) c(1, 1, FALSE) else c(0 , 0, FALSE)),
  pessoa[-(1:3)]
)
pessoa__
trajectory: pessoa, 10 activities
{ Activity: SetAttribute | keys: [tipo_de_consulta], values: function(), global: 0, mod: N, init: 0 }
{ Activity: SetAttribute | [contacto] keys: [tipo_de_contacto], values: function(), global: 0, mod: N, init: 0 }
{ Activity: SetPrior     | values: function(), mod: N }
{ Activity: RenegeIn     | t: function(), keep_seized: 0 }
  Fork 1, stop,  trajectory: anonymous, 1 activities
  { Activity: SetAttribute | keys: [phone_rejected], values: [1], global: 0, mod: N, init: 0 }
{ Activity: Seize        | resource: administrador, amount: 1 }
{ Activity: RenegeAbort  |  }
{ Activity: SetAttribute | keys: [atendido], values: [1], global: 0, mod: N, init: 0 }
{ Activity: Timeout      | delay: function() }
{ Activity: Release      | resource: administrador, amount: 1 }
envs <- lapply(1:50, function(i) {
  env <<- simmer("hospital") 
  env %>% 
    add_generator("pessoa_de_manha", pessoa__, from_to(8*60, 10*60, \() rexp(1, (120/2)/60), every = 24*60, arrive = F), mon = 2) %>% 
    add_generator("pessoa_de_tarde", pessoa__, from_to(10*60, 16*60, \() rexp(1, (240/6)/60), every = 24*60, arrive = F), mon = 2) %>% 
    add_generator("pessoa_de_noite", pessoa__, from_to(16*60, 18*60, \() rexp(1, (40/2)/60), every = 24*60, arrive = F), mon = 2) %>% 
    add_resource("administrador", schedule(
      c(8 , 9 , 9.5 , 12 , 15.5 ,18)*60, 
      c(  1 , 2,    4,   3,     1,  0), 
      period = 24*60
    )) %>%
    run(24*60*5)
})

envs %>% simmer.plot::get_mon_arrivals(ongoing = T) -> arrivals
envs %>% simmer.plot::get_mon_attributes() -> attributes
envs %>% simmer.plot::get_mon_resources() -> resources
arrivals %>%
  filter(start_time != -1) %>% # n sei pq e q isto acontece
  arrange(start_time) %>% 
  select(-activity_time) %>% 
  mutate(
    day = ceiling(start_time/(24*60)),
    start_time_day = start_time %% (24*60) %>% seconds_to_period(),
    end_time_day = end_time %% (24*60) %>% seconds_to_period(),
    replication = replication %>% as_factor(),
  ) -> arrivals.df2a
arrivals.df2a %>% filter(replication == 1) %>% mutate(start_time_day = start_time_day %>% round(3), end_time_day = end_time_day %>% round(3)) 
arrivals.df2a$replication <- as.integer(as.character(arrivals.df2a$replication))

# analise de eficiencia
attributes %>% 
  left_join(arrivals.df2a, by = c("name", "replication") ) %>% 
  select(name, key, value, replication, day, end_time, end_time_day) -> atributes.df2a
atributes.df2a %>% 
  filter(key == "phone_rejected")
# analise de eficiencia
# numero de chamadas perdidas
# add day to atributes from arrivals.df2a
attributes %>% 
  left_join(arrivals.df2a %>% select(name, replication, day, start_time, end_time, end_time_day), by = c("name", "replication")) %>% 
  rename(atribute_time = time, pessoa_start_time = start_time, pessoa_end_time = end_time) %>% 
  select(atribute_time, name, key, value, replication, day, pessoa_start_time, pessoa_end_time) -> attributes.df2a
attributes.df2a 
# pessoas e os seus tipos de chamada
attributes.df2a %>% 
  filter(key == "tipo_de_contacto") %>% 
  select(name, replication, value) %>% 
  mutate(value = ifelse(value == tipo_de_contacto.TELEFONE, "telefone", "app")) %>%
  rename(contacto = value) -> contactos.df2a
# percentagem de chamadas perdidas
attributes.df2a %>% 
  filter(key == "tipo_de_contacto", value == tipo_de_contacto.TELEFONE) %>% 
  left_join(attributes.df2a %>% filter(key == "phone_rejected") %>% mutate(gave_up = key) %>% select(name, replication, gave_up), join_by("name", "replication")) %>% 
  mutate(gave_up = if_else(gave_up %>% is.na, T, F), replication = replication %>% as_factor) %>% 
  select(name, replication, pessoa_end_time, gave_up) -> phone_calls
phone_calls
# phone_calls rate per replication
phone_calls %>% 
  group_by(replication) %>% 
  summarise(phone_calls = n(), gave_up = sum(gave_up)) %>% 
  mutate(phone_calls_per_hour = phone_calls/10, gave_up_rate = gave_up/phone_calls) %>% 
  arrange(desc(gave_up_rate)) -> phone_calls_rate
phone_calls_rate

phone_calls_rate %>% 
  ggplot(aes(x = reorder(replication, gave_up_rate), y = gave_up_rate)) +
  geom_col() + 
  ylim(0, 1) +
  theme(axis.text.x = element_text(angle = 70, hjust = 1)) +
  geom_hline(aes(yintercept = min(gave_up_rate)), linetype = "dashed", color = "red") +
  geom_hline(aes(yintercept = max(gave_up_rate)), linetype = "dashed", color = "blue") +
  xlab("Replication") + 
  ylab("Gave up rate")

phone_calls_rate %>% 
  summarise(
    min = min(phone_calls_per_hour), 
    mean = mean(phone_calls_per_hour), 
    max = max(phone_calls_per_hour)
    )
# evolucao ao longo do dia DO QUE?
attributes.df2a %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none")

# get queue size histogram
resources %>% 
  mutate(day = ceiling(time/(24*60))) %>% 
  group_by(replication) %>%
  reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
  mutate(replication = replication %>% as_factor()) %>% 
  ggplot(aes(x = time, y = queue_size)) +
  geom_step(aes(color = replication), alpha = 0.3) + 
  geom_smooth(method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none")


resources %>% 
  mutate(day = ceiling(time/(24*60))) %>% 
  group_by(replication) %>%
  reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
  mutate(replication = replication %>% as_factor()) %>% 
  ggplot(aes(x = time, y = queue_size)) +
  geom_step(aes(color = replication), alpha = 0.3) + 
  geom_smooth(method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  ylim(0,250)

NA
# join these 2 plots
attributes.df2a %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  geom_step(data = resources %>% 
              mutate(day = ceiling(time/(24*60))) %>% 
              group_by(replication) %>%
              reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
              mutate(replication = replication %>% as_factor()), 
            aes(x = time, y = queue_size), alpha = 0.3) + 
  geom_smooth(data = resources %>% 
                mutate(day = ceiling(time/(24*60))) %>% 
                group_by(replication) %>%
                reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
                mutate(replication = replication %>% as_factor()), 
              aes(x = time, y = queue_size), method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  ylim(0,875)

  
attributes.df2a %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none") +
  geom_step(data = resources %>% 
              mutate(day = ceiling(time/(24*60))) %>% 
              group_by(replication) %>%
              reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
              mutate(replication = replication %>% as_factor()), 
            aes(x = time, y = queue_size), alpha = 0.3) + 
  geom_smooth(data = resources %>% 
                mutate(day = ceiling(time/(24*60))) %>% 
                group_by(replication) %>%
                reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
                mutate(replication = replication %>% as_factor()), 
              aes(x = time, y = queue_size), method="lm") +
  facet_wrap(vars(day), scale = "free") +
  theme(legend.position = "none")



attributes.df2a %>% 
  filter(key == "phone_rejected") %>% 
  mutate(day = day %>% as_factor(), replication = replication %>% as_factor) %>% 
  group_by(replication) %>% 
  reframe(time_rejected = pessoa_end_time, sumcum_per_day = cumsum(value), day = day) %>% 
  ggplot(aes(x = time_rejected %>% as.integer(), y = sumcum_per_day)) +
  geom_line(aes(color = replication), alpha = 0.3) +
  geom_smooth() +

  theme(legend.position = "none") +
  geom_step(data = resources %>% 
              mutate(day = ceiling(time/(24*60))) %>% 
              group_by(replication) %>%
              reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
              mutate(replication = replication %>% as_factor()), 
            aes(x = time, y = queue_size), alpha = 0.3) + 
  geom_smooth(data = resources %>% 
                mutate(day = ceiling(time/(24*60))) %>% 
                group_by(replication) %>%
                reframe(day = day %>% as_factor(), time = time, queue_size = queue) %>% 
                mutate(replication = replication %>% as_factor()), 
              aes(x = time, y = queue_size), method="lm") +

  theme(legend.position = "none")

# tempo de espera daqueles que concluiram, speardao por chamada e mensagem
attributes %>% 
  filter(key == "atendido") %>% 
  left_join(arrivals.df2a %>% select(start_time, name, replication), join_by("name", "replication")) %>% 
  left_join(contactos.df2a, join_by("name", "replication")) %>% 
  mutate(time_waiting = time - start_time) %>% 
  select(name, value, replication, time_waiting, contacto) -> waiting_times
waiting_times
# histograma (n tou a perceber pq é q há waiting times maiores que 5)
waiting_times %>% 
  filter(contacto == "telefone") %>%
  ggplot(aes(x = time_waiting)) +
  geom_histogram(bins = 100) +
  theme(legend.position = "none")+ 
  ylim(0, 1000)
ggsave("2a_HistTel.svg")
Saving 7.29 x 4.5 in image

waiting_times %>% 
  filter(contacto == "app") %>%
  ggplot(aes(x = time_waiting)) +
  geom_histogram(bins = 100) +
  theme(legend.position = "none")

# regular waiting time stats without graphs
waiting_times %>% 
  group_by(contacto) %>% 
  summarise(mean = mean(time_waiting), sd = sd(time_waiting), median = median(time_waiting)) 
waiting_times %>% 
  group_by(contacto) %>% 
  summarise(quantile_10 = quantile(time_waiting, 0.1), quantile_20 = quantile(time_waiting, 0.2), quantile_30 = quantile(time_waiting, 0.3), quantile_40 = quantile(time_waiting, 0.4), quantile_50 = quantile(time_waiting, 0.5), quantile_60 = quantile(time_waiting, 0.6), quantile_70 = quantile(time_waiting, 0.7), quantile_80 = quantile(time_waiting, 0.8), quantile_90 = quantile(time_waiting, 0.9), quantile_95 = quantile(time_waiting, 0.95), quantile_99 = quantile(time_waiting, 0.99))
resources %>% plot(metric = "utilization")

resources %>% plot(metric = "usage", steps = T)

LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCmlmIChGQUxTRSkgew0KICBpbnN0YWxsUGFja2FnZXNOZWVkZWQoKQ0KfQ0KYGBgDQoNCg0KIyMgUHJvY2Vzc28NCg0KVW0gcGFjaWVudGUgcXVlIGVzdMOhIGVtIGxpc3RhIGRlIGVzcGVyYSBwYXJhIHNlciBvcGVyYWRvOg0KDQoxLiBFbnRyYSBlbSBjb250YWN0byBjb20gbyBzZXJ2acOnbyBkZSBjaXJ1cmdpYSwgdsOhcmlvcyBkaWFzIGFudGVzIGRhIHN1YSBvcGVyYcOnw6NvLCANCiAgKiBBdHJhdsOpcyBkZSB1bWEgY2hhbWFkYSB0ZWxlZsOzbmljYSBvdSANCiAgKiBBdHJhdsOpcyBkbyBlbnZpbyBkZSB1bWEgbWVuc2FnZW0gZXNjcml0YSBudW1hIGFwcC4NCg0KMi4gT3Mgc2VydmnDp29zIGFkbWluaXN0cmF0aXZvcyBhdGVuZGVtIGFzIGNoYW1hZGFzIG91IGzDqmVtIGEgbWVuc2FnZW0gdmlhIGFwcCBlLCBtZWRpYW50ZQ0KICAqIGEgaW5mb3JtYcOnw6NvIHByZXNlbnRlIG5vIHNpc3RlbWEgZGFkYSBwZWxvIG3DqWRpY28gYXNzaXN0ZW50ZSBlIA0KICAqIGEgaW5mb3JtYcOnw6NvIGZvcm5lY2lkYSBwZWxvIHBhY2llbnRlLCANCm8gYWRtaW5pc3RyYXRpdm8gZGVjaWRlIHNlIGFnZW5kYQ0KICAqIHVtYSBjb25zdWx0YSBwcmVzZW5jaWFsIGFudGVzIGRhIGNpcnVyZ2lhLCBvdQ0KICAqIHVtYSBjb25zdWx0YSB0ZWxlZsOzbmljYSwgb3UNCiAgKiBuw6NvIGjDoSBuZWNlc3NpZGFkZSBkZSBxdWFscXVlciBjb25zdWx0YS4gDQoNCkNvbnR1ZG8sIGVzdGUgcHJvY2VkaW1lbnRvIG5lY2Vzc2l0YSBkZSBtZWxob3JpYXMuIA0KTyBvYmplY3Rpdm8gZGVzdGUgZXN0dWRvIMOpIG9wdGltaXphciBhIGdlc3TDo28gZG9zIGNvbnRhY3RvcyAodGVtcG8gdG90YWwgZG8gcHJvY2Vzc28gZSB0b3RhbCBkZSBjaGFtYWRhcyBwZXJkaWRhcykNCg0KIyMgU2l0dWHDp8OjbyBhdHVhbA0KDQoxLiBBcGVuYXMgMiBhZG1pbmlzdHJhdGl2b3Mgbm8gc2VydmnDp28sIGFtYm9zIGNvbWXDp2FtIMOgcyA4aCBlIGFjYWJhbSDDoHMgMThoICgxMGggZGUgdHJhYmFsaG8pIGRlIGNhZGEgZGlhIMO6dGlsLg0KDQoyLiBPIHRlbXBvIGVudHJlIGNoZWdhZGFzIHRlbSBleHAoMS80MCkgKHBxIG1lZGlhIGRlIDQwL2gpDQoNCjMuIFRpcG8gZGUgY29udGFjdG86DQoqIHByb2IgZGUgdGVsZWZvbmUgPSAwLjg1DQoqIHByb2IgZGUgYXBwID0gMC4xNQ0KDQo0LiBUaXBvIGRlIGNvbnN1bHRhOiANCiogMC4zIG7Do28gw6kgYWdlbmRhZG8gY29uc3VsdGEsIA0KKiAwLjYgY29uc3VsYSBwb3IgdGVsZWZvbmUsIA0KKiAwLjEgcHJlc2VuY2lhbA0KDQo1LiBUZW1wbyBkZSANCiogQ29udmVyc2EgYW50ZXMgZGEgZGVjaXNhbyDDqSBub3JtKDQsMV4yKQ0KKiBUb21hZGEgZGUgZGVjaXNhbyBkbyBhZG1pbnN0cmF0aXZvIMOpIG5vcm0oMSwwLjI1XjIpDQoqIFRlbXBvIGRlIGFnZW5kYW1lbnRvIFNFIE5FQ0VTU8OBUklPIMOpIG5vcm0oMSwwLjI1XjIpDQoNCjYuIDVvIHRlbGVmb25lIGRlc2xpZ2EgYXBvcyA1IG1pbnV0b3MgZGUgZXNwZXJhIChlIGZpY2EgY2hhbWFkYSBwZXJkaWRhKSANCg0KDQpgYGB7cn0NCmxpYnJhcnkoc2ltbWVyKQ0KbGlicmFyeShzaW1tZXIuYnJpY2tzKQ0KbGlicmFyeShzaW1tZXIucGxvdCkNCmNvbmZsaWN0c19wcmVmZXIoc2ltbWVyOjpyb2xsYmFjaykNCmNvbmZsaWN0c19wcmVmZXIoc2ltbWVyOjpub3cpDQpjb25mbGljdHNfcHJlZmVyKGRwbHlyOjpzZWxlY3QpDQojIHRoaXMgd2lsbCBiZSBzdWJzdGl0dXRlZCBiZWxvdyBhbmQgbGl0ZXJhbGx5IGp1c3QgaXMgaGVyZSBiZWNhdXNlIHNpbW1lciBkb2Vzbid0IGtub3cgaG93IHRvIGltcGxlbWVudCBwaXBlcyBhbmQgSSBoYXRlIGl0IGhlcmUNCmVudiA8LSBzaW1tZXIoImhvc3BpdGFsIikgDQpgYGANClJlc291cmNlczoNCiAgU2VydmVyOiBPIGhvc3BpdGFsIGNvbSAyIGFkbWluaXN0cmFkb3Jlcw0KICBRdWV1ZTogRmlsYSBkZSBlc3BlcmEgY29tIHByaW9yaWRhZGUgZGUgY2xpZW50cyBlbSBjaGFtYWRhDQogICAgIE7Do28gaMOhIHZhbnRnZW0gZW0gZXNjb2xoZXIgYSBhcHAgcXVhbmRvIGjDoSBjaGFtYWRhIGRlIGVzcGVyYQ0KTWFuYWdlcjogdmFpIGF0cmlidWlyIG8gdGlwbyBkZSBjb25zdWx0YSAoZSBWYWkgYWx0ZXJhciBvIHRpcG8gZGUgY2xpZW50ZSBzZSBlbGUgZm9yIHJlamVpdGFkbyBuYSBjaGFtYWRhPykNCkdlcmFkb3I6IHZhaSBnZXJpciBub3ZvcyBhcnJpdmFscyAoZXhwKDEvNDApKQ0KYXJyaXZhbDogY2FkYSBwYWNpZW50ZSAocXVlIGxldmEgdW1hIHRyYWplY3RvcnkpDQp0cmFqZWN0b3J5OiBhIHJlY2VpdGEgZGUgY2FkYSBhcnJpdmFsDQoNCmBgYHtyfQ0KIyBhdHJpYnV0ZXMgbmVlZCB0byBiZSBudW1lcmljDQp0aXBvX2RlX2NvbnN1bHRhLlBSRVNFTkNJQUwgPC0gMQ0KdGlwb19kZV9jb25zdWx0YS5URUxFRk9OSUNBIDwtIDINCnRpcG9fZGVfY29uc3VsdGEuTkFPX0FHRU5EQURBIDwtIDMNCnRpcG9zX2RlX2NvbnN1bHRhIDwtIGModGlwb19kZV9jb25zdWx0YS5QUkVTRU5DSUFMLCB0aXBvX2RlX2NvbnN1bHRhLlRFTEVGT05JQ0EsIHRpcG9fZGVfY29uc3VsdGEuTkFPX0FHRU5EQURBKQ0KdGlwb3NfZGVfY29uc3VsdGEucHJvYnMgPC0gYygwLjEsIDAuNiwgMC4zKQ0KDQp0aXBvX2RlX2NvbnRhY3RvLlRFTEVGT05FIDwtIDENCnRpcG9fZGVfY29udGFjdG8uQVBQIDwtIDINCnRpcG9zX2RlX2NvbnRhY3RvIDwtIGModGlwb19kZV9jb250YWN0by5URUxFRk9ORSwgdGlwb19kZV9jb250YWN0by5BUFApDQp0aXBvc19kZV9jb250YWN0by5wcm9icyA8LSBjKDAuODUsIDAuMTUpDQoNCnRlbXBvX2F0ZW5kaWRvIDwtIGZ1bmN0aW9uKCkgew0KICBtZWFuX3RpbWVfdHJpYWdlbSA8LSBybm9ybSgxLCBtZWFuID0gNCwgc2QgPSAxKQ0KICBtZWFuX3RpbWVfZGVjaXNpb24gPC0gcm5vcm0oMSwgbWVhbiA9IDEsIHNkID0gMC4yNSkNCiAgdGltZV90b190aW1lb3V0IDwtIG1lYW5fdGltZV90cmlhZ2VtICsgbWVhbl90aW1lX2RlY2lzaW9uDQogIA0KICBpZiAoZ2V0X2F0dHJpYnV0ZShlbnYsICJ0aXBvX2RlX2NvbnN1bHRhIikgIT0gdGlwb19kZV9jb25zdWx0YS5OQU9fQUdFTkRBREEpIHsNCiAgICB0aW1lX3RvX3RpbWVvdXQgPC0gdGltZV90b190aW1lb3V0ICsgcm5vcm0oMSwgbWVhbiA9IDEsIHNkID0gMC4yNSkNCiAgfQ0KICANCiAgcmV0dXJuKHRpbWVfdG9fdGltZW91dCkNCn0NCg0KaW50aW1lIDwtIGZ1bmN0aW9uKCkgew0KICBub3coZW52KSUlIDI0KjYwIC0+IG4NCiAgcmV0dXJuKDgqNjAgPCBuICYgbiA8IDE4KjYwKQ0KfQ0KYGBgDQoNCg0KYGBge3J9DQpwZXNzb2EgPC0gdHJhamVjdG9yeSgicGVzc29hIikgJT4lDQogICMgc2V0X2F0dHJpYnV0ZSgidGltZXNfcGhvbmVfcmVqZWN0ZWQiLCAwKSAlPiUgDQogICMgc2V0X2F0dHJpYnV0ZSgicGhvbmVfcmVqZWN0ZWQiLCAwKSAlPiUNCiAgc2V0X2F0dHJpYnV0ZSgidGlwb19kZV9jb25zdWx0YSIsIFwoKSBzYW1wbGUodGlwb3NfZGVfY29uc3VsdGEsIDEsIHByb2IgPSB0aXBvc19kZV9jb25zdWx0YS5wcm9icykpICU+JQ0KICBzZXRfYXR0cmlidXRlKCJ0aXBvX2RlX2NvbnRhY3RvIiwgXCgpIHNhbXBsZSh0aXBvc19kZV9jb250YWN0bywgMSwgcHJvYiA9IHRpcG9zX2RlX2NvbnRhY3RvLnByb2JzKSwgdGFnID0gImNvbnRhY3RvIikgJT4lDQogIHNldF9wcmlvcml0aXphdGlvbihcKCkgaWYgKGdldF9hdHRyaWJ1dGUoZW52LCAidGlwb19kZV9jb250YWN0byIpID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUpIGMoMSwgLTEsIEZBTFNFKSBlbHNlIGMoMCAsIC0xLCBGQUxTRSkpICU+JSAjIFBSSU9SSVRZLCBEUk9QIFBSSU9SSVRZLCBSRVNUQVJUDQogICMgbG9nXyhcKCkgcGFzdGUoIlZvdSBwcmEgZmlsYSBhZ29yYSwgZXN0b3UgYSB1c2FyIiwgaWZlbHNlKGdldF9hdHRyaWJ1dGUoZW52LCAidGlwb19kZV9jb250YWN0byIpID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUsICJvIHRlbGVmb25lIiwgImEgYXBwIikpKSAlPiUNCiAgcmVuZWdlX2luKFwoKSBpZmVsc2UoZ2V0X2F0dHJpYnV0ZShlbnYsICJ0aXBvX2RlX2NvbnRhY3RvIikgPT0gdGlwb19kZV9jb250YWN0by5URUxFRk9ORSwgNSwgSW5mKSwgb3V0ID0gDQogICAgICAgICAgICAgIHRyYWplY3RvcnkoKSAlPiUgc2V0X2F0dHJpYnV0ZSgicGhvbmVfcmVqZWN0ZWQiLCAxKQ0KICAgICAgICAgICAgICAjIE8gYm90YXMgbsOjbyBnb3N0b3UNCiAgICAgICAgICAgICAgIyB0cmFqZWN0b3J5KCJwZXNzb2FfdHJ5X2FnYWluIikgJT4lIA0KICAgICAgICAgICAgICAjIHNldF9hdHRyaWJ1dGUoInRpbWVzX3Bob25lX3JlamVjdGVkIiwgXCgpIGdldF9hdHRyaWJ1dGUoZW52LCAidGltZXNfcGhvbmVfcmVqZWN0ZWQiKSArIDEpICU+JSANCiAgICAgICAgICAgICAgIyAjIGxlYXZlIGlmIG91dCBvZiBzY2hlZHVsZQ0KICAgICAgICAgICAgICAjIGxlYXZlKFwoKSB7aWZlbHNlKGludGltZSgpLCAwLCAxKX0pICU+JSANCiAgICAgICAgICAgICAgIyAjIGxvZ18oXCgpICJOYW8gZnVpIGF0ZW5kaWRvLCB2b3Ugdm9sdGFyIHByYSBmaWxhIikgJT4lIA0KICAgICAgICAgICAgICAjIHJvbGxiYWNrKHRhcmdldD0iY29udGFjdG8iLCB0aW1lcyA9IDEpICMgbsOjbyBmdW5jaW9uYSwgbXVkYXIgcHJhIG51bWVybz8NCiAgICAgICAgICAgICkgJT4lDQogIHNlaXplKCJhZG1pbmlzdHJhZG9yIiwgMSkgJT4lDQogIHJlbmVnZV9hYm9ydCgpICU+JQ0KICAjIGxvZ18oXCgpIHBhc3RlKCJCZWZvcmUgdGltZW91dCIpKSAlPiUgDQogIHNldF9hdHRyaWJ1dGUoImF0ZW5kaWRvIiwgMSkgJT4lIA0KICB0aW1lb3V0KHRlbXBvX2F0ZW5kaWRvKSAlPiUgDQogICMgbG9nXyhcKCkgIkFmdGVyIHRpbWVvdXQiKSAlPiUgDQogIHJlbGVhc2UoImFkbWluaXN0cmFkb3IiLCAxKQ0KICAjIGxvZ18oXCgpICJMZWF2aW5nIikNCg0KcGVzc29hDQpgYGANCg0KDQpgYGB7cn0NCnNldC5zZWVkKDEpDQojIDUwIHJlcGxpY2FzDQplbnZzIDwtIGxhcHBseSgxOjUwLCBmdW5jdGlvbihpKSB7DQogIGVudiA8PC0gc2ltbWVyKCJob3NwaXRhbCIpIA0KICBlbnYgJT4lIA0KICAgIGFkZF9nZW5lcmF0b3IoInBlc3NvYSIsIHBlc3NvYSwgZnJvbV90byg4KjYwLCAxOCo2MCwgXCgpIHJleHAoMSwgNDAvNjApLCBldmVyeSA9IDI0KjYwLCBhcnJpdmUgPSBGKSwgbW9uID0gMikgJT4lIA0KICAgIGFkZF9yZXNvdXJjZSgiYWRtaW5pc3RyYWRvciIsIHNjaGVkdWxlKGMoOCo2MCwgMTgqNjApLCBjKDIsIDApLCBwZXJpb2QgPSAyNCo2MCkpICU+JQ0KICAgIHJ1bigyNCo2MCo1KQ0KfSkNCg0KZW52cyAlPiUgc2ltbWVyLnBsb3Q6OmdldF9tb25fYXJyaXZhbHMob25nb2luZyA9IFQpIC0+IGFycml2YWxzIA0KZW52cyAlPiUgc2ltbWVyLnBsb3Q6OmdldF9tb25fYXR0cmlidXRlcygpIC0+IGF0dHJpYnV0ZXMNCmVudnMgJT4lIHNpbW1lci5wbG90OjpnZXRfbW9uX3Jlc291cmNlcygpIC0+IHJlc291cmNlcw0KYGBgDQoNCg0KYGBge3J9DQoNCmFycml2YWxzICU+JQ0KICBmaWx0ZXIoc3RhcnRfdGltZSAhPSAtMSkgJT4lICMgbiBzZWkgcHEgZSBxIGlzdG8gYWNvbnRlY2UNCiAgYXJyYW5nZShzdGFydF90aW1lKSAlPiUgDQogIHNlbGVjdCgtYWN0aXZpdHlfdGltZSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGF5ID0gY2VpbGluZyhzdGFydF90aW1lLygyNCo2MCkpLA0KICAgIHN0YXJ0X3RpbWVfZGF5ID0gc3RhcnRfdGltZSAlJSAoMjQqNjApICU+JSBzZWNvbmRzX3RvX3BlcmlvZCgpLA0KICAgIGVuZF90aW1lX2RheSA9IGVuZF90aW1lICUlICgyNCo2MCkgJT4lIHNlY29uZHNfdG9fcGVyaW9kKCksDQogICAgIyByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSwNCiAgKSAtPiBhcnJpdmFscy5kZjENCmFycml2YWxzLmRmMSAlPiUgZmlsdGVyKHJlcGxpY2F0aW9uID09IDEpICU+JSBtdXRhdGUoc3RhcnRfdGltZV9kYXkgPSBzdGFydF90aW1lX2RheSAlPiUgcm91bmQoMyksIGVuZF90aW1lX2RheSA9IGVuZF90aW1lX2RheSAlPiUgcm91bmQoMykpDQoNCg0KDQojIHR1cm4gd2hhdCdzIGFib3ZlIGludG8gYSBmdW5jdGlvbiBzbyBpdCBjYW4gYmUgZG9uZSBmb3IgMmEgYW5kIDJiDQojIGdldF9hcnJpdmFscyA8LSBmdW5jdGlvbihtb25fYXR0cmlidXRlcykgew0KIyAgIG1vbl9hdHRyaWJ1dGVzICU+JQ0KIyAgICAgZmlsdGVyKHN0YXJ0X3RpbWUgIT0gLTEpICU+JSAjIG4gc2VpIHBxIGUgcSBpc3RvIGFjb250ZWNlDQojICAgICBhcnJhbmdlKHN0YXJ0X3RpbWUpICU+JSANCiMgICAgIHNlbGVjdCgtYWN0aXZpdHlfdGltZSkgJT4lIA0KIyAgICAgbXV0YXRlKA0KIyAgICAgICBkYXkgPSBjZWlsaW5nKHN0YXJ0X3RpbWUvKDI0KjYwKSksDQojICAgICAgIHN0YXJ0X3RpbWVfZGF5ID0gc3RhcnRfdGltZSAlJSAoMjQqNjApICU+JSBzZWNvbmRzX3RvX3BlcmlvZCgpLA0KIyAgICAgICBlbmRfdGltZV9kYXkgPSBlbmRfdGltZSAlJSAoMjQqNjApICU+JSBzZWNvbmRzX3RvX3BlcmlvZCgpLA0KIyAgICAgICAjIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpLA0KIyAgICAgKQ0KIyB9DQojIA0KIyBwcHJpbnRfYXJyaXZhbHMgPC0gZnVuY3Rpb24gKGFycml2YWxzLmRmMSkgew0KIyAgIGFycml2YWxzLmRmMSAlPiUgZmlsdGVyKHJlcGxpY2F0aW9uID09IDEpICU+JSBtdXRhdGUoc3RhcnRfdGltZV9kYXkgPSBzdGFydF90aW1lX2RheSAlPiUgcm91bmQoMyksIGVuZF90aW1lX2RheSA9ICMgZW5kX3RpbWVfZGF5ICU+JSByb3VuZCgzKSkNCiMgfQ0KDQpgYGANCg0KYGBge3J9DQojIGFuYWxpc2UgZGUgZWZpY2llbmNpYQ0KIyBudW1lcm8gZGUgY2hhbWFkYXMgcGVyZGlkYXMNCiMgYWRkIGRheSB0byBhdHJpYnV0ZXMgZnJvbSBhcnJpdmFscy5kZjENCmF0dHJpYnV0ZXMgJT4lIA0KICBsZWZ0X2pvaW4oYXJyaXZhbHMuZGYxICU+JSBzZWxlY3QobmFtZSwgcmVwbGljYXRpb24sIGRheSwgc3RhcnRfdGltZSwgZW5kX3RpbWUsIGVuZF90aW1lX2RheSksIGJ5ID0gYygibmFtZSIsICJyZXBsaWNhdGlvbiIpKSAlPiUgDQogIHJlbmFtZShhdHJpYnV0ZV90aW1lID0gdGltZSwgcGVzc29hX3N0YXJ0X3RpbWUgPSBzdGFydF90aW1lLCBwZXNzb2FfZW5kX3RpbWUgPSBlbmRfdGltZSkgJT4lIA0KICBzZWxlY3QoYXRyaWJ1dGVfdGltZSwgbmFtZSwga2V5LCB2YWx1ZSwgcmVwbGljYXRpb24sIGRheSwgcGVzc29hX3N0YXJ0X3RpbWUsIHBlc3NvYV9lbmRfdGltZSkgLT4gYXR0cmlidXRlcy5kZjENCmF0dHJpYnV0ZXMuZGYxIA0KYGBgDQoNCmBgYHtyfQ0KIyBwZXNzb2FzIGUgb3Mgc2V1cyB0aXBvcyBkZSBjaGFtYWRhDQphdHRyaWJ1dGVzLmRmMSAlPiUgDQogIGZpbHRlcihrZXkgPT0gInRpcG9fZGVfY29udGFjdG8iKSAlPiUgDQogIHNlbGVjdChuYW1lLCByZXBsaWNhdGlvbiwgdmFsdWUpICU+JSANCiAgbXV0YXRlKHZhbHVlID0gaWZlbHNlKHZhbHVlID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUsICJ0ZWxlZm9uZSIsICJhcHAiKSkgJT4lDQogIHJlbmFtZShjb250YWN0byA9IHZhbHVlKSAtPiBjb250YWN0b3MuZGYxDQpgYGANCg0KDQpgYGB7cn0NCiMgcGVyY2VudGFnZW0gZGUgY2hhbWFkYXMgcGVyZGlkYXMNCmF0dHJpYnV0ZXMuZGYxICU+JSANCiAgZmlsdGVyKGtleSA9PSAidGlwb19kZV9jb250YWN0byIsIHZhbHVlID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUpICU+JSANCiAgbGVmdF9qb2luKGF0dHJpYnV0ZXMuZGYxICU+JSBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSBtdXRhdGUoZ2F2ZV91cCA9IGtleSkgJT4lIHNlbGVjdChuYW1lLCByZXBsaWNhdGlvbiwgZ2F2ZV91cCksIGpvaW5fYnkoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICBtdXRhdGUoZ2F2ZV91cCA9IGlmX2Vsc2UoZ2F2ZV91cCAlPiUgaXMubmEsIFQsIEYpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgc2VsZWN0KG5hbWUsIHJlcGxpY2F0aW9uLCBwZXNzb2FfZW5kX3RpbWUsIGdhdmVfdXApIC0+IHBob25lX2NhbGxzDQpwaG9uZV9jYWxscw0KYGBgDQoNCmBgYHtyfQ0KIyBwaG9uZV9jYWxscyByYXRlIHBlciByZXBsaWNhdGlvbg0KcGhvbmVfY2FsbHMgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICBzdW1tYXJpc2UocGhvbmVfY2FsbHMgPSBuKCksIGdhdmVfdXAgPSBzdW0oZ2F2ZV91cCkpICU+JSANCiAgbXV0YXRlKHBob25lX2NhbGxzX3Blcl9ob3VyID0gcGhvbmVfY2FsbHMvMTAsIGdhdmVfdXBfcmF0ZSA9IGdhdmVfdXAvcGhvbmVfY2FsbHMpICU+JSANCiAgYXJyYW5nZShkZXNjKGdhdmVfdXBfcmF0ZSkpIC0+IHBob25lX2NhbGxzX3JhdGUNCnBob25lX2NhbGxzX3JhdGUNCg0KcGhvbmVfY2FsbHNfcmF0ZSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIocmVwbGljYXRpb24sIGdhdmVfdXBfcmF0ZSksIHkgPSBnYXZlX3VwX3JhdGUpKSArDQogIGdlb21fY29sKCkgKyANCiAgeWxpbSgwLCAxKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNzAsIGhqdXN0ID0gMSkpICsNCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1pbihnYXZlX3VwX3JhdGUpKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKw0KICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWF4KGdhdmVfdXBfcmF0ZSkpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibHVlIikgKw0KICB4bGFiKCJSZXBsaWNhdGlvbiIpICsgDQogIHlsYWIoIkdhdmUgdXAgcmF0ZSIpDQoNCmdnc2F2ZSgiZ2F2ZV91cF9yYXRlLnN2ZyIpDQpgYGANCg0KYGBge3J9DQpwaG9uZV9jYWxsc19yYXRlICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIG1pbiA9IG1pbihwaG9uZV9jYWxsc19wZXJfaG91ciksIA0KICAgIG1lYW4gPSBtZWFuKHBob25lX2NhbGxzX3Blcl9ob3VyKSwgDQogICAgbWF4ID0gbWF4KHBob25lX2NhbGxzX3Blcl9ob3VyKQ0KICAgICkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBldm9sdWNhbyBhbyBsb25nbyBkbyBkaWEgRE8gUVVFPw0KYXR0cmlidXRlcy5kZjEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQgJT4lIGFzLmludGVnZXIoKSwgeSA9IHN1bWN1bV9wZXJfZGF5KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBnZXQgcXVldWUgc2l6ZSBoaXN0b2dyYW0NCnJlc291cmNlcyAlPiUgDQogIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSkgKw0KICBnZW9tX3N0ZXAoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KcmVzb3VyY2VzICU+JSANCiAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpKSArDQogIGdlb21fc3RlcChhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArIA0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIHlsaW0oMCwyNTApDQogIA0KDQpgYGANCg0KDQpgYGB7cn0NCiMgam9pbiB0aGVzZSAyIHBsb3RzDQphdHRyaWJ1dGVzLmRmMSAlPiUgDQogIGZpbHRlcihrZXkgPT0gInBob25lX3JlamVjdGVkIikgJT4lIA0KICBtdXRhdGUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUgDQogIHJlZnJhbWUodGltZV9yZWplY3RlZCA9IHBlc3NvYV9lbmRfdGltZSwgc3VtY3VtX3Blcl9kYXkgPSBjdW1zdW0odmFsdWUpLCBkYXkgPSBkYXkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZV9yZWplY3RlZCAlPiUgYXMuaW50ZWdlcigpLCB5ID0gc3VtY3VtX3Blcl9kYXkpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdlb21fc3RlcChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgeWxpbSgwLDg3NSkNCiAgDQphdHRyaWJ1dGVzLmRmMSAlPiUgDQogIGZpbHRlcihrZXkgPT0gInBob25lX3JlamVjdGVkIikgJT4lIA0KICBtdXRhdGUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUgDQogIHJlZnJhbWUodGltZV9yZWplY3RlZCA9IHBlc3NvYV9lbmRfdGltZSwgc3VtY3VtX3Blcl9kYXkgPSBjdW1zdW0odmFsdWUpLCBkYXkgPSBkYXkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZV9yZWplY3RlZCAlPiUgYXMuaW50ZWdlcigpLCB5ID0gc3VtY3VtX3Blcl9kYXkpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdlb21fc3RlcChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCg0KYXR0cmlidXRlcy5kZjEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQgJT4lIGFzLmludGVnZXIoKSwgeSA9IHN1bWN1bV9wZXJfZGF5KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKw0KICBnZW9tX3Ntb290aCgpICsNCg0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgZ2VvbV9zdGVwKGRhdGEgPSByZXNvdXJjZXMgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZSwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSksIA0KICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIGFscGhhID0gMC4zKSArIA0KICBnZW9tX3Ntb290aChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICAgICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICAgIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSksIA0KICAgICAgICAgICAgICBhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSwgbWV0aG9kPSJsbSIpICsNCg0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KYGBge3J9DQojIHRlbXBvIGRlIGVzcGVyYSBkYXF1ZWxlcyBxdWUgY29uY2x1aXJhbSwgc3BlYXJkYW8gcG9yIGNoYW1hZGEgZSBtZW5zYWdlbQ0KYXR0cmlidXRlcyAlPiUgDQogIGZpbHRlcihrZXkgPT0gImF0ZW5kaWRvIikgJT4lIA0KICBsZWZ0X2pvaW4oYXJyaXZhbHMuZGYxICU+JSBzZWxlY3Qoc3RhcnRfdGltZSwgbmFtZSwgcmVwbGljYXRpb24pLCBqb2luX2J5KCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgbGVmdF9qb2luKGNvbnRhY3Rvcy5kZjEsIGpvaW5fYnkoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICBtdXRhdGUodGltZV93YWl0aW5nID0gdGltZSAtIHN0YXJ0X3RpbWUpICU+JSANCiAgc2VsZWN0KG5hbWUsIHZhbHVlLCByZXBsaWNhdGlvbiwgdGltZV93YWl0aW5nLCBjb250YWN0bykgLT4gd2FpdGluZ190aW1lcw0Kd2FpdGluZ190aW1lcw0KYGBgDQoNCg0KYGBge3J9DQojIGhpc3RvZ3JhbWEgKG4gdG91IGEgcGVyY2ViZXIgcHEgw6kgcSBow6Egd2FpdGluZyB0aW1lcyBtYWlvcmVzIHF1ZSA1KQ0Kd2FpdGluZ190aW1lcyAlPiUgDQogIGZpbHRlcihjb250YWN0byA9PSAidGVsZWZvbmUiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gdGltZV93YWl0aW5nKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0Kd2FpdGluZ190aW1lcyAlPiUgDQogIGZpbHRlcihjb250YWN0byA9PSAiYXBwIikgJT4lDQogIGdncGxvdChhZXMoeCA9IHRpbWVfd2FpdGluZykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwMCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KDQpgYGB7cn0NCiMgcmVndWxhciB3YWl0aW5nIHRpbWUgc3RhdHMgd2l0aG91dCBncmFwaHMNCndhaXRpbmdfdGltZXMgJT4lIA0KICBncm91cF9ieShjb250YWN0bykgJT4lIA0KICBzdW1tYXJpc2UobWVhbiA9IG1lYW4odGltZV93YWl0aW5nKSwgc2QgPSBzZCh0aW1lX3dhaXRpbmcpLCBtZWRpYW4gPSBtZWRpYW4odGltZV93YWl0aW5nKSkgDQp3YWl0aW5nX3RpbWVzICU+JSANCiAgZ3JvdXBfYnkoY29udGFjdG8pICU+JSANCiAgc3VtbWFyaXNlKHF1YW50aWxlXzEwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjEpLCBxdWFudGlsZV8yMCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC4yKSwgcXVhbnRpbGVfMzAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuMyksIHF1YW50aWxlXzQwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjQpLCBxdWFudGlsZV81MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC41KSwgcXVhbnRpbGVfNjAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuNiksIHF1YW50aWxlXzcwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjcpLCBxdWFudGlsZV84MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC44KSwgcXVhbnRpbGVfOTAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuOSksIHF1YW50aWxlXzk1ID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjk1KSwgcXVhbnRpbGVfOTkgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuOTkpKQ0KYGBgDQoNCg0KYGBge3J9DQpyZXNvdXJjZXMgJT4lIHBsb3QobWV0cmljID0gInV0aWxpemF0aW9uIikNCnJlc291cmNlcyAlPiUgcGxvdChtZXRyaWMgPSAidXNhZ2UiLCBzdGVwcyA9IFQpDQoNCmBgYA0KIyMgUGVyZ3VudGEgMiANCg0KQWzDrW5lYXMgY29tIG11ZGFuw6dhcyByZWxhdGl2YXMgw6BzIGludGVycnVww6fDtWVzDQoNCmBgYHtyfQ0KIyBtdWRhbsOnYXMNCmVudnMgPC0gbGFwcGx5KDE6NTAsIGZ1bmN0aW9uKGkpIHsNCiAgZW52IDw8LSBzaW1tZXIoImhvc3BpdGFsIikgDQogIGVudiAlPiUgDQogICAgYWRkX2dlbmVyYXRvcigicGVzc29hX2RlX21hbmhhIiwgcGVzc29hLCBmcm9tX3RvKDgqNjAsIDEwKjYwLCBcKCkgcmV4cCgxLCAoMTIwLzIpLzYwKSwgZXZlcnkgPSAyNCo2MCwgYXJyaXZlID0gRiksIG1vbiA9IDIpICU+JSANCiAgICBhZGRfZ2VuZXJhdG9yKCJwZXNzb2FfZGVfdGFyZGUiLCBwZXNzb2EsIGZyb21fdG8oMTAqNjAsIDE2KjYwLCBcKCkgcmV4cCgxLCAoMjQwLzYpLzYwKSwgZXZlcnkgPSAyNCo2MCwgYXJyaXZlID0gRiksIG1vbiA9IDIpICU+JSANCiAgICBhZGRfZ2VuZXJhdG9yKCJwZXNzb2FfZGVfbm9pdGUiLCBwZXNzb2EsIGZyb21fdG8oMTYqNjAsIDE4KjYwLCBcKCkgcmV4cCgxLCAoNDAvMikvNjApLCBldmVyeSA9IDI0KjYwLCBhcnJpdmUgPSBGKSwgbW9uID0gMikgJT4lIA0KICAgIGFkZF9yZXNvdXJjZSgiYWRtaW5pc3RyYWRvciIsIHNjaGVkdWxlKA0KICAgICAgYyg4ICwgOSAsIDkuNSAsIDEyICwgMTUuNSAsMTgpKjYwLCANCiAgICAgIGMoICAxICwgMiwgICAgNCwgICAzLCAgICAgMSwgIDApLCANCiAgICAgIHBlcmlvZCA9IDI0KjYwDQogICAgKSkgJT4lDQogICAgcnVuKDI0KjYwKjUpDQp9KQ0KDQplbnZzICU+JSBzaW1tZXIucGxvdDo6Z2V0X21vbl9hcnJpdmFscyhvbmdvaW5nID0gVCkgLT4gYXJyaXZhbHMNCmVudnMgJT4lIHNpbW1lci5wbG90OjpnZXRfbW9uX2F0dHJpYnV0ZXMoKSAtPiBhdHRyaWJ1dGVzDQplbnZzICU+JSBzaW1tZXIucGxvdDo6Z2V0X21vbl9yZXNvdXJjZXMoKSAtPiByZXNvdXJjZXMNCmBgYA0KDQojIyAyLmIpDQoNCmBgYHtyfQ0KIyBiKQ0KYXJyaXZhbHMgJT4lDQogIGZpbHRlcihzdGFydF90aW1lICE9IC0xKSAlPiUgIyBuIHNlaSBwcSBlIHEgaXN0byBhY29udGVjZQ0KICBhcnJhbmdlKHN0YXJ0X3RpbWUpICU+JSANCiAgc2VsZWN0KC1hY3Rpdml0eV90aW1lKSAlPiUgDQogIG11dGF0ZSgNCiAgICBkYXkgPSBjZWlsaW5nKHN0YXJ0X3RpbWUvKDI0KjYwKSksDQogICAgc3RhcnRfdGltZV9kYXkgPSBzdGFydF90aW1lICUlICgyNCo2MCkgJT4lIHNlY29uZHNfdG9fcGVyaW9kKCksDQogICAgZW5kX3RpbWVfZGF5ID0gZW5kX3RpbWUgJSUgKDI0KjYwKSAlPiUgc2Vjb25kc190b19wZXJpb2QoKSwNCiAgICByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSwNCiAgKSAtPiBhcnJpdmFscy5kZjJiDQphcnJpdmFscy5kZjJiICU+JSBmaWx0ZXIocmVwbGljYXRpb24gPT0gMSkgJT4lIG11dGF0ZShzdGFydF90aW1lX2RheSA9IHN0YXJ0X3RpbWVfZGF5ICU+JSByb3VuZCgzKSwgZW5kX3RpbWVfZGF5ID0gZW5kX3RpbWVfZGF5ICU+JSByb3VuZCgzKSkNCmBgYA0KDQpgYGB7cn0NCmFycml2YWxzLmRmMmIkcmVwbGljYXRpb24gPC0gYXMuaW50ZWdlcihhcy5jaGFyYWN0ZXIoYXJyaXZhbHMuZGYyYiRyZXBsaWNhdGlvbikpDQoNCiMgdGVtcG8gZGUgZXNwZXJhIGRhcXVlbGVzIHF1ZSBjb25jbHVpcmFtLCBzcGVhcmRhbyBwb3IgY2hhbWFkYSBlIG1lbnNhZ2VtDQphcnJpdmFscy5kZjJiICU+JSANCiAgZmlsdGVyKGZpbmlzaGVkID09IFRSVUUpICU+JSANCiAgbXV0YXRlKHdhaXRpbmdfdGltZSA9IGVuZF90aW1lIC0gc3RhcnRfdGltZSkgJT4lIA0KICBsZWZ0X2pvaW4oYXR0cmlidXRlcyAlPiUgZmlsdGVyKGtleSA9PSAidGlwb19kZV9jb250YWN0byIpLCBieSA9IGMoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICBtdXRhdGUodGlwb19kZV9jb250YWN0byA9IGlmZWxzZSh2YWx1ZSA9PSB0aXBvX2RlX2NvbnRhY3RvLlRFTEVGT05FLCAidGVsZWZvbmUiLCAibWVuc2FnZW0iKSAlPiUgYXNfZmFjdG9yLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JQ0KICBzZWxlY3QobmFtZSwgcmVwbGljYXRpb24sIHdhaXRpbmdfdGltZSwgdGlwb19kZV9jb250YWN0bykgLT4gd2FpdGluZ190aW1lcy4yYg0Kd2FpdGluZ190aW1lcy4yYg0KYGBgDQoNCmBgYHtyfQ0KIyBhbmFsaXNlIGRlIGVmaWNpZW5jaWENCiMgbnVtZXJvIGRlIGNoYW1hZGFzIHBlcmRpZGFzDQojIGFkZCBkYXkgdG8gYXRyaWJ1dGVzIGZyb20gYXJyaXZhbHMuZGYyYg0KYXR0cmlidXRlcyAlPiUgDQogIGxlZnRfam9pbihhcnJpdmFscy5kZjJiICU+JSBzZWxlY3QobmFtZSwgcmVwbGljYXRpb24sIGRheSwgc3RhcnRfdGltZSwgZW5kX3RpbWUsIGVuZF90aW1lX2RheSksIGJ5ID0gYygibmFtZSIsICJyZXBsaWNhdGlvbiIpKSAlPiUgDQogIHJlbmFtZShhdHJpYnV0ZV90aW1lID0gdGltZSwgcGVzc29hX3N0YXJ0X3RpbWUgPSBzdGFydF90aW1lLCBwZXNzb2FfZW5kX3RpbWUgPSBlbmRfdGltZSkgJT4lIA0KICBzZWxlY3QoYXRyaWJ1dGVfdGltZSwgbmFtZSwga2V5LCB2YWx1ZSwgcmVwbGljYXRpb24sIGRheSwgcGVzc29hX3N0YXJ0X3RpbWUsIHBlc3NvYV9lbmRfdGltZSkgLT4gYXR0cmlidXRlcy5kZjJiDQphdHRyaWJ1dGVzLmRmMmIgDQpgYGANCg0KYGBge3J9DQojIHBlc3NvYXMgZSBvcyBzZXVzIHRpcG9zIGRlIGNoYW1hZGENCmF0dHJpYnV0ZXMuZGYyYiAlPiUgDQogIGZpbHRlcihrZXkgPT0gInRpcG9fZGVfY29udGFjdG8iKSAlPiUgDQogIHNlbGVjdChuYW1lLCByZXBsaWNhdGlvbiwgdmFsdWUpICU+JSANCiAgbXV0YXRlKHZhbHVlID0gaWZlbHNlKHZhbHVlID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUsICJ0ZWxlZm9uZSIsICJhcHAiKSkgJT4lDQogIHJlbmFtZShjb250YWN0byA9IHZhbHVlKSAtPiBjb250YWN0b3MuZGYyYg0KYGBgDQoNCg0KYGBge3J9DQojIHBlcmNlbnRhZ2VtIGRlIGNoYW1hZGFzIHBlcmRpZGFzDQphdHRyaWJ1dGVzLmRmMmIgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJ0aXBvX2RlX2NvbnRhY3RvIiwgdmFsdWUgPT0gdGlwb19kZV9jb250YWN0by5URUxFRk9ORSkgJT4lIA0KICBsZWZ0X2pvaW4oYXR0cmlidXRlcy5kZjJiICU+JSBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSBtdXRhdGUoZ2F2ZV91cCA9IGtleSkgJT4lIHNlbGVjdChuYW1lLCByZXBsaWNhdGlvbiwgZ2F2ZV91cCksIGpvaW5fYnkoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICBtdXRhdGUoZ2F2ZV91cCA9IGlmX2Vsc2UoZ2F2ZV91cCAlPiUgaXMubmEsIFQsIEYpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgc2VsZWN0KG5hbWUsIHJlcGxpY2F0aW9uLCBwZXNzb2FfZW5kX3RpbWUsIGdhdmVfdXApIC0+IHBob25lX2NhbGxzDQpwaG9uZV9jYWxscw0KYGBgDQoNCmBgYHtyfQ0KIyBwaG9uZV9jYWxscyByYXRlIHBlciByZXBsaWNhdGlvbg0KcGhvbmVfY2FsbHMgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICBzdW1tYXJpc2UocGhvbmVfY2FsbHMgPSBuKCksIGdhdmVfdXAgPSBzdW0oZ2F2ZV91cCkpICU+JSANCiAgbXV0YXRlKHBob25lX2NhbGxzX3Blcl9ob3VyID0gcGhvbmVfY2FsbHMvMTAsIGdhdmVfdXBfcmF0ZSA9IGdhdmVfdXAvcGhvbmVfY2FsbHMpICU+JSANCiAgYXJyYW5nZShkZXNjKGdhdmVfdXBfcmF0ZSkpIC0+IHBob25lX2NhbGxzX3JhdGUNCnBob25lX2NhbGxzX3JhdGUNCg0KcGhvbmVfY2FsbHNfcmF0ZSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHJlb3JkZXIocmVwbGljYXRpb24sIGdhdmVfdXBfcmF0ZSksIHkgPSBnYXZlX3VwX3JhdGUpKSArDQogIGdlb21fY29sKCkgKyANCiAgeWxpbSgwLCAxKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNzAsIGhqdXN0ID0gMSkpICsNCiAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IG1pbihnYXZlX3VwX3JhdGUpKSwgbGluZXR5cGUgPSAiZGFzaGVkIiwgY29sb3IgPSAicmVkIikgKw0KICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWF4KGdhdmVfdXBfcmF0ZSkpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJibHVlIikgKw0KICB4bGFiKCJSZXBsaWNhdGlvbiIpICsgDQogIHlsYWIoIkdhdmUgdXAgcmF0ZSIpDQoNCmdnc2F2ZSgiZ2F2ZV91cF9yYXRlLnN2ZyIpDQpgYGANCg0KYGBge3J9DQpwaG9uZV9jYWxsc19yYXRlICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIG1pbiA9IG1pbihwaG9uZV9jYWxsc19wZXJfaG91ciksIA0KICAgIG1lYW4gPSBtZWFuKHBob25lX2NhbGxzX3Blcl9ob3VyKSwgDQogICAgbWF4ID0gbWF4KHBob25lX2NhbGxzX3Blcl9ob3VyKQ0KICAgICkNCmBgYA0KDQoNCmBgYHtyfQ0KIyBldm9sdWNhbyBhbyBsb25nbyBkbyBkaWEgRE8gUVVFPw0KYXR0cmlidXRlcy5kZjJiICU+JSANCiAgZmlsdGVyKGtleSA9PSAicGhvbmVfcmVqZWN0ZWQiKSAlPiUgDQogIG11dGF0ZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JSANCiAgcmVmcmFtZSh0aW1lX3JlamVjdGVkID0gcGVzc29hX2VuZF90aW1lLCBzdW1jdW1fcGVyX2RheSA9IGN1bXN1bSh2YWx1ZSksIGRheSA9IGRheSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3JlamVjdGVkICU+JSBhcy5pbnRlZ2VyKCksIHkgPSBzdW1jdW1fcGVyX2RheSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KDQpgYGB7cn0NCiMgZ2V0IHF1ZXVlIHNpemUgaGlzdG9ncmFtDQpyZXNvdXJjZXMgJT4lIA0KICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZSwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSkpICsNCiAgZ2VvbV9zdGVwKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsgDQogIGdlb21fc21vb3RoKG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCnJlc291cmNlcyAlPiUgDQogIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSkgKw0KICBnZW9tX3N0ZXAoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICB5bGltKDAsMjUwKQ0KICANCmBgYA0KDQoNCmBgYHtyfQ0KIyBqb2luIHRoZXNlIDIgcGxvdHMNCmF0dHJpYnV0ZXMuZGYyYiAlPiUgDQogIGZpbHRlcihrZXkgPT0gInBob25lX3JlamVjdGVkIikgJT4lIA0KICBtdXRhdGUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUgDQogIHJlZnJhbWUodGltZV9yZWplY3RlZCA9IHBlc3NvYV9lbmRfdGltZSwgc3VtY3VtX3Blcl9kYXkgPSBjdW1zdW0odmFsdWUpLCBkYXkgPSBkYXkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZV9yZWplY3RlZCAlPiUgYXMuaW50ZWdlcigpLCB5ID0gc3VtY3VtX3Blcl9kYXkpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdlb21fc3RlcChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgeWxpbSgwLDg3NSkNCiAgDQphdHRyaWJ1dGVzLmRmMmIgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQgJT4lIGFzLmludGVnZXIoKSwgeSA9IHN1bWN1bV9wZXJfZGF5KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBnZW9tX3N0ZXAoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICBhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSwgYWxwaGEgPSAwLjMpICsgDQogIGdlb21fc21vb3RoKGRhdGEgPSByZXNvdXJjZXMgJT4lIA0KICAgICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZSwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBtZXRob2Q9ImxtIikgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KDQoNCmF0dHJpYnV0ZXMuZGYyYiAlPiUgDQogIGZpbHRlcihrZXkgPT0gInBob25lX3JlamVjdGVkIikgJT4lIA0KICBtdXRhdGUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUgDQogIHJlZnJhbWUodGltZV9yZWplY3RlZCA9IHBlc3NvYV9lbmRfdGltZSwgc3VtY3VtX3Blcl9kYXkgPSBjdW1zdW0odmFsdWUpLCBkYXkgPSBkYXkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZV9yZWplY3RlZCAlPiUgYXMuaW50ZWdlcigpLCB5ID0gc3VtY3VtX3Blcl9kYXkpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKCkgKw0KDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBnZW9tX3N0ZXAoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICBhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSwgYWxwaGEgPSAwLjMpICsgDQogIGdlb21fc21vb3RoKGRhdGEgPSByZXNvdXJjZXMgJT4lIA0KICAgICAgICAgICAgICAgIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgICAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZSwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSwgDQogICAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBtZXRob2Q9ImxtIikgKw0KDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQpgYGB7cn0NCiMgdGVtcG8gZGUgZXNwZXJhIGRhcXVlbGVzIHF1ZSBjb25jbHVpcmFtLCBzcGVhcmRhbyBwb3IgY2hhbWFkYSBlIG1lbnNhZ2VtDQphdHRyaWJ1dGVzICU+JSANCiAgZmlsdGVyKGtleSA9PSAiYXRlbmRpZG8iKSAlPiUgDQogIGxlZnRfam9pbihhcnJpdmFscy5kZjJiICU+JSBzZWxlY3Qoc3RhcnRfdGltZSwgbmFtZSwgcmVwbGljYXRpb24pLCBqb2luX2J5KCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgbGVmdF9qb2luKGNvbnRhY3Rvcy5kZjJiLCBqb2luX2J5KCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgbXV0YXRlKHRpbWVfd2FpdGluZyA9IHRpbWUgLSBzdGFydF90aW1lKSAlPiUgDQogIHNlbGVjdChuYW1lLCB2YWx1ZSwgcmVwbGljYXRpb24sIHRpbWVfd2FpdGluZywgY29udGFjdG8pIC0+IHdhaXRpbmdfdGltZXMNCndhaXRpbmdfdGltZXMNCmBgYA0KDQoNCmBgYHtyfQ0KIyBoaXN0b2dyYW1hIChuIHRvdSBhIHBlcmNlYmVyIHBxIMOpIHEgaMOhIHdhaXRpbmcgdGltZXMgbWFpb3JlcyBxdWUgNSkNCndhaXRpbmdfdGltZXMgJT4lIA0KICBmaWx0ZXIoY29udGFjdG8gPT0gInRlbGVmb25lIikgJT4lDQogIGdncGxvdChhZXMoeCA9IHRpbWVfd2FpdGluZykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwMCkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpKw0KICB5bGltKDAsIDEwMDApDQoNCndhaXRpbmdfdGltZXMgJT4lIA0KICBmaWx0ZXIoY29udGFjdG8gPT0gImFwcCIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3dhaXRpbmcpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDApICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCg0KYGBge3J9DQojIHJlZ3VsYXIgd2FpdGluZyB0aW1lIHN0YXRzIHdpdGhvdXQgZ3JhcGhzDQp3YWl0aW5nX3RpbWVzICU+JSANCiAgZ3JvdXBfYnkoY29udGFjdG8pICU+JSANCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKHRpbWVfd2FpdGluZyksIHNkID0gc2QodGltZV93YWl0aW5nKSwgbWVkaWFuID0gbWVkaWFuKHRpbWVfd2FpdGluZykpIA0Kd2FpdGluZ190aW1lcyAlPiUgDQogIGdyb3VwX2J5KGNvbnRhY3RvKSAlPiUgDQogIHN1bW1hcmlzZShxdWFudGlsZV8xMCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC4xKSwgcXVhbnRpbGVfMjAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuMiksIHF1YW50aWxlXzMwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjMpLCBxdWFudGlsZV80MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC40KSwgcXVhbnRpbGVfNTAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuNSksIHF1YW50aWxlXzYwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjYpLCBxdWFudGlsZV83MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC43KSwgcXVhbnRpbGVfODAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuOCksIHF1YW50aWxlXzkwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjkpLCBxdWFudGlsZV85NSA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC45NSksIHF1YW50aWxlXzk5ID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjk5KSkNCmBgYA0KDQoNCmBgYHtyfQ0KcmVzb3VyY2VzICU+JSBwbG90KG1ldHJpYyA9ICJ1dGlsaXphdGlvbiIpDQpyZXNvdXJjZXMgJT4lIHBsb3QobWV0cmljID0gInVzYWdlIiwgc3RlcHMgPSBUKQ0KYGBgDQoNCiMjIDIuYSkgLSAtIC0gLSAtIC0gLSAtIC0gLSAtIC0NCg0KYGBge3J9DQojIGEpIHNldF9wcmlvcml0aXphdGlvbihcKCkgaWYgKGdldF9hdHRyaWJ1dGUoZW52LCAidGlwb19kZV9jb25zdWx0YSIpID09IHRpcG9fZGVfY29uc3VsdGEuVEVMRUZPTklDQSkgYygxLCAtMSwgRkFMU0UpIGVsc2UgYygwICwgLTEsIEZBTFNFKSkNCnBlc3NvYV9fIDwtIGpvaW4oDQogIHBlc3NvYVsxOjJdLCANCiAgdHJhamVjdG9yeSgiY2hhbmdlX3ByaW8iKSAlPiUgDQogICAgIHNldF9wcmlvcml0aXphdGlvbihcKCkgaWYgKGdldF9hdHRyaWJ1dGUoZW52LCAidGlwb19kZV9jb250YWN0byIpID09IHRpcG9fZGVfY29udGFjdG8uVEVMRUZPTkUpIGMoMSwgMSwgRkFMU0UpIGVsc2UgYygwICwgMCwgRkFMU0UpKSwNCiAgcGVzc29hWy0oMTozKV0NCikNCnBlc3NvYV9fDQpgYGANCg0KDQpgYGB7cn0NCmVudnMgPC0gbGFwcGx5KDE6NTAsIGZ1bmN0aW9uKGkpIHsNCiAgZW52IDw8LSBzaW1tZXIoImhvc3BpdGFsIikgDQogIGVudiAlPiUgDQogICAgYWRkX2dlbmVyYXRvcigicGVzc29hX2RlX21hbmhhIiwgcGVzc29hX18sIGZyb21fdG8oOCo2MCwgMTAqNjAsIFwoKSByZXhwKDEsICgxMjAvMikvNjApLCBldmVyeSA9IDI0KjYwLCBhcnJpdmUgPSBGKSwgbW9uID0gMikgJT4lIA0KICAgIGFkZF9nZW5lcmF0b3IoInBlc3NvYV9kZV90YXJkZSIsIHBlc3NvYV9fLCBmcm9tX3RvKDEwKjYwLCAxNio2MCwgXCgpIHJleHAoMSwgKDI0MC82KS82MCksIGV2ZXJ5ID0gMjQqNjAsIGFycml2ZSA9IEYpLCBtb24gPSAyKSAlPiUgDQogICAgYWRkX2dlbmVyYXRvcigicGVzc29hX2RlX25vaXRlIiwgcGVzc29hX18sIGZyb21fdG8oMTYqNjAsIDE4KjYwLCBcKCkgcmV4cCgxLCAoNDAvMikvNjApLCBldmVyeSA9IDI0KjYwLCBhcnJpdmUgPSBGKSwgbW9uID0gMikgJT4lIA0KICAgIGFkZF9yZXNvdXJjZSgiYWRtaW5pc3RyYWRvciIsIHNjaGVkdWxlKA0KICAgICAgYyg4ICwgOSAsIDkuNSAsIDEyICwgMTUuNSAsMTgpKjYwLCANCiAgICAgIGMoICAxICwgMiwgICAgNCwgICAzLCAgICAgMSwgIDApLCANCiAgICAgIHBlcmlvZCA9IDI0KjYwDQogICAgKSkgJT4lDQogICAgcnVuKDI0KjYwKjUpDQp9KQ0KDQplbnZzICU+JSBzaW1tZXIucGxvdDo6Z2V0X21vbl9hcnJpdmFscyhvbmdvaW5nID0gVCkgLT4gYXJyaXZhbHMNCmVudnMgJT4lIHNpbW1lci5wbG90OjpnZXRfbW9uX2F0dHJpYnV0ZXMoKSAtPiBhdHRyaWJ1dGVzDQplbnZzICU+JSBzaW1tZXIucGxvdDo6Z2V0X21vbl9yZXNvdXJjZXMoKSAtPiByZXNvdXJjZXMNCmBgYA0KDQpgYGB7cn0NCmFycml2YWxzICU+JQ0KICBmaWx0ZXIoc3RhcnRfdGltZSAhPSAtMSkgJT4lICMgbiBzZWkgcHEgZSBxIGlzdG8gYWNvbnRlY2UNCiAgYXJyYW5nZShzdGFydF90aW1lKSAlPiUgDQogIHNlbGVjdCgtYWN0aXZpdHlfdGltZSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGF5ID0gY2VpbGluZyhzdGFydF90aW1lLygyNCo2MCkpLA0KICAgIHN0YXJ0X3RpbWVfZGF5ID0gc3RhcnRfdGltZSAlJSAoMjQqNjApICU+JSBzZWNvbmRzX3RvX3BlcmlvZCgpLA0KICAgIGVuZF90aW1lX2RheSA9IGVuZF90aW1lICUlICgyNCo2MCkgJT4lIHNlY29uZHNfdG9fcGVyaW9kKCksDQogICAgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCksDQogICkgLT4gYXJyaXZhbHMuZGYyYQ0KYXJyaXZhbHMuZGYyYSAlPiUgZmlsdGVyKHJlcGxpY2F0aW9uID09IDEpICU+JSBtdXRhdGUoc3RhcnRfdGltZV9kYXkgPSBzdGFydF90aW1lX2RheSAlPiUgcm91bmQoMyksIGVuZF90aW1lX2RheSA9IGVuZF90aW1lX2RheSAlPiUgcm91bmQoMykpIA0KYGBgDQoNCmBgYHtyfQ0KYXJyaXZhbHMuZGYyYSRyZXBsaWNhdGlvbiA8LSBhcy5pbnRlZ2VyKGFzLmNoYXJhY3RlcihhcnJpdmFscy5kZjJhJHJlcGxpY2F0aW9uKSkNCg0KIyBhbmFsaXNlIGRlIGVmaWNpZW5jaWENCmF0dHJpYnV0ZXMgJT4lIA0KICBsZWZ0X2pvaW4oYXJyaXZhbHMuZGYyYSwgYnkgPSBjKCJuYW1lIiwgInJlcGxpY2F0aW9uIikgKSAlPiUgDQogIHNlbGVjdChuYW1lLCBrZXksIHZhbHVlLCByZXBsaWNhdGlvbiwgZGF5LCBlbmRfdGltZSwgZW5kX3RpbWVfZGF5KSAtPiBhdHJpYnV0ZXMuZGYyYQ0KYXRyaWJ1dGVzLmRmMmEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpDQpgYGANCg0KYGBge3J9DQojIGFuYWxpc2UgZGUgZWZpY2llbmNpYQ0KIyBudW1lcm8gZGUgY2hhbWFkYXMgcGVyZGlkYXMNCiMgYWRkIGRheSB0byBhdHJpYnV0ZXMgZnJvbSBhcnJpdmFscy5kZjJhDQphdHRyaWJ1dGVzICU+JSANCiAgbGVmdF9qb2luKGFycml2YWxzLmRmMmEgJT4lIHNlbGVjdChuYW1lLCByZXBsaWNhdGlvbiwgZGF5LCBzdGFydF90aW1lLCBlbmRfdGltZSwgZW5kX3RpbWVfZGF5KSwgYnkgPSBjKCJuYW1lIiwgInJlcGxpY2F0aW9uIikpICU+JSANCiAgcmVuYW1lKGF0cmlidXRlX3RpbWUgPSB0aW1lLCBwZXNzb2Ffc3RhcnRfdGltZSA9IHN0YXJ0X3RpbWUsIHBlc3NvYV9lbmRfdGltZSA9IGVuZF90aW1lKSAlPiUgDQogIHNlbGVjdChhdHJpYnV0ZV90aW1lLCBuYW1lLCBrZXksIHZhbHVlLCByZXBsaWNhdGlvbiwgZGF5LCBwZXNzb2Ffc3RhcnRfdGltZSwgcGVzc29hX2VuZF90aW1lKSAtPiBhdHRyaWJ1dGVzLmRmMmENCmF0dHJpYnV0ZXMuZGYyYSANCmBgYA0KDQpgYGB7cn0NCiMgcGVzc29hcyBlIG9zIHNldXMgdGlwb3MgZGUgY2hhbWFkYQ0KYXR0cmlidXRlcy5kZjJhICU+JSANCiAgZmlsdGVyKGtleSA9PSAidGlwb19kZV9jb250YWN0byIpICU+JSANCiAgc2VsZWN0KG5hbWUsIHJlcGxpY2F0aW9uLCB2YWx1ZSkgJT4lIA0KICBtdXRhdGUodmFsdWUgPSBpZmVsc2UodmFsdWUgPT0gdGlwb19kZV9jb250YWN0by5URUxFRk9ORSwgInRlbGVmb25lIiwgImFwcCIpKSAlPiUNCiAgcmVuYW1lKGNvbnRhY3RvID0gdmFsdWUpIC0+IGNvbnRhY3Rvcy5kZjJhDQpgYGANCg0KDQpgYGB7cn0NCiMgcGVyY2VudGFnZW0gZGUgY2hhbWFkYXMgcGVyZGlkYXMNCmF0dHJpYnV0ZXMuZGYyYSAlPiUgDQogIGZpbHRlcihrZXkgPT0gInRpcG9fZGVfY29udGFjdG8iLCB2YWx1ZSA9PSB0aXBvX2RlX2NvbnRhY3RvLlRFTEVGT05FKSAlPiUgDQogIGxlZnRfam9pbihhdHRyaWJ1dGVzLmRmMmEgJT4lIGZpbHRlcihrZXkgPT0gInBob25lX3JlamVjdGVkIikgJT4lIG11dGF0ZShnYXZlX3VwID0ga2V5KSAlPiUgc2VsZWN0KG5hbWUsIHJlcGxpY2F0aW9uLCBnYXZlX3VwKSwgam9pbl9ieSgibmFtZSIsICJyZXBsaWNhdGlvbiIpKSAlPiUgDQogIG11dGF0ZShnYXZlX3VwID0gaWZfZWxzZShnYXZlX3VwICU+JSBpcy5uYSwgVCwgRiksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBzZWxlY3QobmFtZSwgcmVwbGljYXRpb24sIHBlc3NvYV9lbmRfdGltZSwgZ2F2ZV91cCkgLT4gcGhvbmVfY2FsbHMNCnBob25lX2NhbGxzDQpgYGANCg0KYGBge3J9DQojIHBob25lX2NhbGxzIHJhdGUgcGVyIHJlcGxpY2F0aW9uDQpwaG9uZV9jYWxscyAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUgDQogIHN1bW1hcmlzZShwaG9uZV9jYWxscyA9IG4oKSwgZ2F2ZV91cCA9IHN1bShnYXZlX3VwKSkgJT4lIA0KICBtdXRhdGUocGhvbmVfY2FsbHNfcGVyX2hvdXIgPSBwaG9uZV9jYWxscy8xMCwgZ2F2ZV91cF9yYXRlID0gZ2F2ZV91cC9waG9uZV9jYWxscykgJT4lIA0KICBhcnJhbmdlKGRlc2MoZ2F2ZV91cF9yYXRlKSkgLT4gcGhvbmVfY2FsbHNfcmF0ZQ0KcGhvbmVfY2FsbHNfcmF0ZQ0KDQpwaG9uZV9jYWxsc19yYXRlICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihyZXBsaWNhdGlvbiwgZ2F2ZV91cF9yYXRlKSwgeSA9IGdhdmVfdXBfcmF0ZSkpICsNCiAgZ2VvbV9jb2woKSArIA0KICB5bGltKDAsIDEpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA3MCwgaGp1c3QgPSAxKSkgKw0KICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gbWluKGdhdmVfdXBfcmF0ZSkpLCBsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvciA9ICJyZWQiKSArDQogIGdlb21faGxpbmUoYWVzKHlpbnRlcmNlcHQgPSBtYXgoZ2F2ZV91cF9yYXRlKSksIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsdWUiKSArDQogIHhsYWIoIlJlcGxpY2F0aW9uIikgKyANCiAgeWxhYigiR2F2ZSB1cCByYXRlIikNCmBgYA0KDQpgYGB7cn0NCnBob25lX2NhbGxzX3JhdGUgJT4lIA0KICBzdW1tYXJpc2UoDQogICAgbWluID0gbWluKHBob25lX2NhbGxzX3Blcl9ob3VyKSwgDQogICAgbWVhbiA9IG1lYW4ocGhvbmVfY2FsbHNfcGVyX2hvdXIpLCANCiAgICBtYXggPSBtYXgocGhvbmVfY2FsbHNfcGVyX2hvdXIpDQogICAgKQ0KYGBgDQoNCg0KYGBge3J9DQojIGV2b2x1Y2FvIGFvIGxvbmdvIGRvIGRpYSBETyBRVUU/DQphdHRyaWJ1dGVzLmRmMmEgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJwaG9uZV9yZWplY3RlZCIpICU+JSANCiAgbXV0YXRlKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcikgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lIA0KICByZWZyYW1lKHRpbWVfcmVqZWN0ZWQgPSBwZXNzb2FfZW5kX3RpbWUsIHN1bWN1bV9wZXJfZGF5ID0gY3Vtc3VtKHZhbHVlKSwgZGF5ID0gZGF5KSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWVfcmVqZWN0ZWQgJT4lIGFzLmludGVnZXIoKSwgeSA9IHN1bWN1bV9wZXJfZGF5KSkgKw0KICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCmBgYA0KDQoNCmBgYHtyfQ0KIyBnZXQgcXVldWUgc2l6ZSBoaXN0b2dyYW0NCnJlc291cmNlcyAlPiUgDQogIG11dGF0ZShkYXkgPSBjZWlsaW5nKHRpbWUvKDI0KjYwKSkpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgbXV0YXRlKHJlcGxpY2F0aW9uID0gcmVwbGljYXRpb24gJT4lIGFzX2ZhY3RvcigpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSkgKw0KICBnZW9tX3N0ZXAoYWVzKGNvbG9yID0gcmVwbGljYXRpb24pLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikNCg0KcmVzb3VyY2VzICU+JSANCiAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpKSArDQogIGdlb21fc3RlcChhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArIA0KICBnZW9tX3Ntb290aChtZXRob2Q9ImxtIikgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIHlsaW0oMCwyNTApDQogIA0KYGBgDQoNCg0KYGBge3J9DQojIGpvaW4gdGhlc2UgMiBwbG90cw0KYXR0cmlidXRlcy5kZjJhICU+JSANCiAgZmlsdGVyKGtleSA9PSAicGhvbmVfcmVqZWN0ZWQiKSAlPiUgDQogIG11dGF0ZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JSANCiAgcmVmcmFtZSh0aW1lX3JlamVjdGVkID0gcGVzc29hX2VuZF90aW1lLCBzdW1jdW1fcGVyX2RheSA9IGN1bXN1bSh2YWx1ZSksIGRheSA9IGRheSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3JlamVjdGVkICU+JSBhcy5pbnRlZ2VyKCksIHkgPSBzdW1jdW1fcGVyX2RheSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgZ2VvbV9zdGVwKGRhdGEgPSByZXNvdXJjZXMgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgcmVmcmFtZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCB0aW1lID0gdGltZSwgcXVldWVfc2l6ZSA9IHF1ZXVlKSAlPiUgDQogICAgICAgICAgICAgIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSksIA0KICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIGFscGhhID0gMC4zKSArIA0KICBnZW9tX3Ntb290aChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUoZGF5ID0gY2VpbGluZyh0aW1lLygyNCo2MCkpKSAlPiUgDQogICAgICAgICAgICAgICAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JQ0KICAgICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICAgIG11dGF0ZShyZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IoKSksIA0KICAgICAgICAgICAgICBhZXMoeCA9IHRpbWUsIHkgPSBxdWV1ZV9zaXplKSwgbWV0aG9kPSJsbSIpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGRheSksIHNjYWxlID0gImZyZWUiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICB5bGltKDAsODc1KQ0KICANCmF0dHJpYnV0ZXMuZGYyYSAlPiUgDQogIGZpbHRlcihrZXkgPT0gInBob25lX3JlamVjdGVkIikgJT4lIA0KICBtdXRhdGUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgcmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKSAlPiUgDQogIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUgDQogIHJlZnJhbWUodGltZV9yZWplY3RlZCA9IHBlc3NvYV9lbmRfdGltZSwgc3VtY3VtX3Blcl9kYXkgPSBjdW1zdW0odmFsdWUpLCBkYXkgPSBkYXkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gdGltZV9yZWplY3RlZCAlPiUgYXMuaW50ZWdlcigpLCB5ID0gc3VtY3VtX3Blcl9kYXkpKSArDQogIGdlb21fbGluZShhZXMoY29sb3IgPSByZXBsaWNhdGlvbiksIGFscGhhID0gMC4zKSArDQogIGdlb21fc21vb3RoKCkgKw0KICBmYWNldF93cmFwKHZhcnMoZGF5KSwgc2NhbGUgPSAiZnJlZSIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdlb21fc3RlcChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIG1ldGhvZD0ibG0iKSArDQogIGZhY2V0X3dyYXAodmFycyhkYXkpLCBzY2FsZSA9ICJmcmVlIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQoNCg0KYXR0cmlidXRlcy5kZjJhICU+JSANCiAgZmlsdGVyKGtleSA9PSAicGhvbmVfcmVqZWN0ZWQiKSAlPiUgDQogIG11dGF0ZShkYXkgPSBkYXkgJT4lIGFzX2ZhY3RvcigpLCByZXBsaWNhdGlvbiA9IHJlcGxpY2F0aW9uICU+JSBhc19mYWN0b3IpICU+JSANCiAgZ3JvdXBfYnkocmVwbGljYXRpb24pICU+JSANCiAgcmVmcmFtZSh0aW1lX3JlamVjdGVkID0gcGVzc29hX2VuZF90aW1lLCBzdW1jdW1fcGVyX2RheSA9IGN1bXN1bSh2YWx1ZSksIGRheSA9IGRheSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3JlamVjdGVkICU+JSBhcy5pbnRlZ2VyKCksIHkgPSBzdW1jdW1fcGVyX2RheSkpICsNCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IHJlcGxpY2F0aW9uKSwgYWxwaGEgPSAwLjMpICsNCiAgZ2VvbV9zbW9vdGgoKSArDQoNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogIGdlb21fc3RlcChkYXRhID0gcmVzb3VyY2VzICU+JSANCiAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICBncm91cF9ieShyZXBsaWNhdGlvbikgJT4lDQogICAgICAgICAgICAgIHJlZnJhbWUoZGF5ID0gZGF5ICU+JSBhc19mYWN0b3IoKSwgdGltZSA9IHRpbWUsIHF1ZXVlX3NpemUgPSBxdWV1ZSkgJT4lIA0KICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgIGFlcyh4ID0gdGltZSwgeSA9IHF1ZXVlX3NpemUpLCBhbHBoYSA9IDAuMykgKyANCiAgZ2VvbV9zbW9vdGgoZGF0YSA9IHJlc291cmNlcyAlPiUgDQogICAgICAgICAgICAgICAgbXV0YXRlKGRheSA9IGNlaWxpbmcodGltZS8oMjQqNjApKSkgJT4lIA0KICAgICAgICAgICAgICAgIGdyb3VwX2J5KHJlcGxpY2F0aW9uKSAlPiUNCiAgICAgICAgICAgICAgICByZWZyYW1lKGRheSA9IGRheSAlPiUgYXNfZmFjdG9yKCksIHRpbWUgPSB0aW1lLCBxdWV1ZV9zaXplID0gcXVldWUpICU+JSANCiAgICAgICAgICAgICAgICBtdXRhdGUocmVwbGljYXRpb24gPSByZXBsaWNhdGlvbiAlPiUgYXNfZmFjdG9yKCkpLCANCiAgICAgICAgICAgICAgYWVzKHggPSB0aW1lLCB5ID0gcXVldWVfc2l6ZSksIG1ldGhvZD0ibG0iKSArDQoNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCmBgYHtyfQ0KIyB0ZW1wbyBkZSBlc3BlcmEgZGFxdWVsZXMgcXVlIGNvbmNsdWlyYW0sIHNwZWFyZGFvIHBvciBjaGFtYWRhIGUgbWVuc2FnZW0NCmF0dHJpYnV0ZXMgJT4lIA0KICBmaWx0ZXIoa2V5ID09ICJhdGVuZGlkbyIpICU+JSANCiAgbGVmdF9qb2luKGFycml2YWxzLmRmMmEgJT4lIHNlbGVjdChzdGFydF90aW1lLCBuYW1lLCByZXBsaWNhdGlvbiksIGpvaW5fYnkoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICBsZWZ0X2pvaW4oY29udGFjdG9zLmRmMmEsIGpvaW5fYnkoIm5hbWUiLCAicmVwbGljYXRpb24iKSkgJT4lIA0KICBtdXRhdGUodGltZV93YWl0aW5nID0gdGltZSAtIHN0YXJ0X3RpbWUpICU+JSANCiAgc2VsZWN0KG5hbWUsIHZhbHVlLCByZXBsaWNhdGlvbiwgdGltZV93YWl0aW5nLCBjb250YWN0bykgLT4gd2FpdGluZ190aW1lcw0Kd2FpdGluZ190aW1lcw0KYGBgDQoNCg0KYGBge3J9DQojIGhpc3RvZ3JhbWEgKG4gdG91IGEgcGVyY2ViZXIgcHEgw6kgcSBow6Egd2FpdGluZyB0aW1lcyBtYWlvcmVzIHF1ZSA1KQ0Kd2FpdGluZ190aW1lcyAlPiUgDQogIGZpbHRlcihjb250YWN0byA9PSAidGVsZWZvbmUiKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gdGltZV93YWl0aW5nKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrIA0KICB5bGltKDAsIDEwMDApDQoNCndhaXRpbmdfdGltZXMgJT4lIA0KICBmaWx0ZXIoY29udGFjdG8gPT0gImFwcCIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lX3dhaXRpbmcpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxMDApICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQ0KYGBgDQoNCg0KYGBge3J9DQojIHJlZ3VsYXIgd2FpdGluZyB0aW1lIHN0YXRzIHdpdGhvdXQgZ3JhcGhzDQp3YWl0aW5nX3RpbWVzICU+JSANCiAgZ3JvdXBfYnkoY29udGFjdG8pICU+JSANCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKHRpbWVfd2FpdGluZyksIHNkID0gc2QodGltZV93YWl0aW5nKSwgbWVkaWFuID0gbWVkaWFuKHRpbWVfd2FpdGluZykpIA0Kd2FpdGluZ190aW1lcyAlPiUgDQogIGdyb3VwX2J5KGNvbnRhY3RvKSAlPiUgDQogIHN1bW1hcmlzZShxdWFudGlsZV8xMCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC4xKSwgcXVhbnRpbGVfMjAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuMiksIHF1YW50aWxlXzMwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjMpLCBxdWFudGlsZV80MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC40KSwgcXVhbnRpbGVfNTAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuNSksIHF1YW50aWxlXzYwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjYpLCBxdWFudGlsZV83MCA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC43KSwgcXVhbnRpbGVfODAgPSBxdWFudGlsZSh0aW1lX3dhaXRpbmcsIDAuOCksIHF1YW50aWxlXzkwID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjkpLCBxdWFudGlsZV85NSA9IHF1YW50aWxlKHRpbWVfd2FpdGluZywgMC45NSksIHF1YW50aWxlXzk5ID0gcXVhbnRpbGUodGltZV93YWl0aW5nLCAwLjk5KSkNCmBgYA0KDQoNCmBgYHtyfQ0KcmVzb3VyY2VzICU+JSBwbG90KG1ldHJpYyA9ICJ1dGlsaXphdGlvbiIpDQpyZXNvdXJjZXMgJT4lIHBsb3QobWV0cmljID0gInVzYWdlIiwgc3RlcHMgPSBUKQ0KYGBgDQoNCg==